Skip to content

Commit a847994

Browse files
committed
Mini NumberTextStyle optimization rework by caching attributes and translation dictionaries.
1 parent 0111399 commit a847994

File tree

16 files changed

+282
-206
lines changed

16 files changed

+282
-206
lines changed

Sources/DiffableTextKitXNumber/Helpers/Reader.swift

Lines changed: 0 additions & 118 deletions
This file was deleted.

Sources/DiffableTextKitXNumber/Models/Adapter.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public struct NumberTextAdapter<Format: NumberTextFormat>: Equatable {
4242
// MARK: Accessors
4343
//=------------------------------------------------------------------------=
4444

45-
@inlinable var lexicon: Lexicon {
46-
scheme.lexicon
45+
@inlinable var reader: Reader {
46+
scheme.reader
4747
}
4848

4949
//=------------------------------------------------------------------------=
@@ -74,15 +74,15 @@ public struct NumberTextAdapter<Format: NumberTextFormat>: Equatable {
7474
@inlinable func autocorrect(_ snapshot: inout Snapshot) {
7575
scheme.autocorrect(&snapshot)
7676
}
77-
78-
@inlinable func number(_ snapshot: Snapshot) throws -> Number {
79-
try Number(in: snapshot, using: scheme.lexicon, as: Value.self)!
80-
}
8177

8278
@inlinable func value(_ number: Number) throws -> Value {
8379
try format.locale(Constants.en_US).parseStrategy.parse(number.description)
8480
}
8581

82+
@inlinable func number(_ snapshot: Snapshot) throws -> Number {
83+
try Number(in: snapshot, using: reader.components, as: Value.self)!
84+
}
85+
8686
//=------------------------------------------------------------------------=
8787
// MARK: Utilities
8888
//=------------------------------------------------------------------------=

Sources/DiffableTextKitXNumber/Number/Number.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ extension Number {
9494

9595
/// Requires that all formatting characters are marked as virtual.
9696
@inlinable init?<T>(in snapshot: Snapshot,
97-
using lexicon: Lexicon, as kind: T.Type)
97+
using components: Components, as kind: T.Type)
9898
throws where T: NumberTextKind {
9999
let unformatted = snapshot.lazy.filter(\.nonvirtual).map(\.character)
100100
try self.init(unformatted: unformatted,
101-
signs: lexicon.signs.components,
102-
digits: lexicon.digits.components,
103-
separators: lexicon.separators.components,
101+
signs: components.signs.components,
102+
digits: components.digits.components,
103+
separators: components.separators.components,
104104
optional: kind.isOptional,
105105
unsigned: kind.isUnsigned,
106106
integer: kind.isInteger )

Sources/DiffableTextKitXNumber/Protocol.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,27 @@ extension NumberTextStyleProtocol {
5151
//=------------------------------------------------------------------------=
5252

5353
@inlinable public var locale: Locale {
54-
adapter.format.locale
54+
format.locale
55+
}
56+
57+
//=------------------------------------------------------------------------=
58+
// MARK: Accessors
59+
//=------------------------------------------------------------------------=
60+
61+
@inlinable var format: Format {
62+
adapter.format
63+
}
64+
65+
@inlinable var reader: Reader {
66+
adapter.reader
5567
}
5668

5769
//=------------------------------------------------------------------------=
5870
// MARK: Utilities
5971
//=------------------------------------------------------------------------=
6072

6173
@inlinable func number(_ proposal: Proposal) throws -> Number? {
62-
try Reader(adapter.lexicon).number(proposal, as: Value.self)
74+
try reader.number(proposal, as: Value.self)
6375
}
6476
}
6577

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//=----------------------------------------------------------------------------=
2+
// This source file is part of the DiffableTextViews open source project.
3+
//
4+
// Copyright (c) 2022 Oscar Byström Ericsson
5+
// Licensed under Apache License, Version 2.0
6+
//
7+
// See http://www.apache.org/licenses/LICENSE-2.0 for license information.
8+
//=----------------------------------------------------------------------------=
9+
10+
import DiffableTextKit
11+
12+
//*============================================================================*
13+
// MARK: * Attributes
14+
//*============================================================================*
15+
16+
@usableFromInline struct Attributes {
17+
@usableFromInline typealias Map = [Character: Attribute]
18+
19+
//=------------------------------------------------------------------------=
20+
// MARK: State
21+
//=------------------------------------------------------------------------=
22+
23+
@usableFromInline let map: Map
24+
25+
//=------------------------------------------------------------------------=
26+
// MARK: Initializers
27+
//=------------------------------------------------------------------------=
28+
29+
@inlinable init(_ local: Components) {
30+
var map = Map(minimumCapacity: 13)
31+
//=--------------------------------------=
32+
// (10) Digits
33+
//=--------------------------------------=
34+
for component in Digit.allCases {
35+
map[local.digits[component]] = .content
36+
}
37+
//=--------------------------------------=
38+
// (1) Separators
39+
//=--------------------------------------=
40+
map[local.separators[.fraction]] = .removable
41+
//=--------------------------------------=
42+
// (2) Signs
43+
//=--------------------------------------=
44+
for component in Sign.allCases {
45+
map[local.signs[component]] = .phantom.subtracting(.virtual)
46+
}
47+
//=--------------------------------------=
48+
// Return
49+
//=--------------------------------------=
50+
self.map = map
51+
}
52+
53+
//=------------------------------------------------------------------------=
54+
// MARK: Accessors
55+
//=------------------------------------------------------------------------=
56+
57+
@inlinable subscript(character: Character) -> Symbol {
58+
Symbol(character, as: map[character] ?? .phantom)
59+
}
60+
}

Sources/DiffableTextKitXNumber/Helpers/Lexicon.swift renamed to Sources/DiffableTextKitXNumber/Reader/Components.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import DiffableTextKit
1111
import Foundation
1212

1313
//*============================================================================*
14-
// MARK: * Lexicon
14+
// MARK: * Components
1515
//*============================================================================*
1616

17-
public final class Lexicon {
17+
@usableFromInline struct Components {
1818
@usableFromInline typealias Signs = Links<Sign>
1919
@usableFromInline typealias Digits = Links<Digit>
2020
@usableFromInline typealias Separators = Links<Separator>
@@ -23,7 +23,7 @@ public final class Lexicon {
2323
// MARK: Instances
2424
//=------------------------------------------------------------------------=
2525

26-
@usableFromInline static let ascii = Lexicon(
26+
@usableFromInline static let ascii = Self(
2727
signs: .ascii(), digits: .ascii(), separators: .ascii())
2828

2929
//=------------------------------------------------------------------------=
@@ -47,18 +47,28 @@ public final class Lexicon {
4747
//=------------------------------------------------------------------------=
4848

4949
/// Requires that formatter.numberStyle == .none.
50-
@inlinable static func standard(_ formatter: NumberFormatter) -> Lexicon {
50+
@inlinable static func standard(_ formatter: NumberFormatter) -> Self {
5151
assert(formatter.numberStyle == .none); return Self.init(
5252
signs: .standard(formatter),
5353
digits: .standard(formatter),
5454
separators: .standard(formatter))
5555
}
5656

5757
/// Requires that formatter.numberStyle == .none.
58-
@inlinable static func currency(_ formatter: NumberFormatter) -> Lexicon {
58+
@inlinable static func currency(_ formatter: NumberFormatter) -> Self {
5959
assert(formatter.numberStyle == .none); return Self.init(
6060
signs: .currency(formatter),
6161
digits: .currency(formatter),
6262
separators: .currency(formatter))
6363
}
64+
65+
//=------------------------------------------------------------------------=
66+
// MARK: Utilities
67+
//=------------------------------------------------------------------------=
68+
69+
@inlinable func consumeSingleSign(in proposal: inout Proposal) -> Sign? {
70+
guard proposal.replacement.count == 1, let sign =
71+
signs[proposal.replacement.first!.character] else { return nil }
72+
proposal.replacement.removeAll(); return sign
73+
}
6474
}
File renamed without changes.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//=----------------------------------------------------------------------------=
2+
// This source file is part of the DiffableTextViews open source project.
3+
//
4+
// Copyright (c) 2022 Oscar Byström Ericsson
5+
// Licensed under Apache License, Version 2.0
6+
//
7+
// See http://www.apache.org/licenses/LICENSE-2.0 for license information.
8+
//=----------------------------------------------------------------------------=
9+
10+
import DiffableTextKit
11+
12+
//*============================================================================*
13+
// MARK: * Reader
14+
//*============================================================================*
15+
16+
public final class Reader {
17+
18+
//=------------------------------------------------------------------------=
19+
// MARK: State
20+
//=------------------------------------------------------------------------=
21+
22+
@usableFromInline let components: Components
23+
@usableFromInline let attributes: Attributes
24+
@usableFromInline let translator: Translator
25+
26+
//=------------------------------------------------------------------------=
27+
// MARK: Initializers
28+
//=------------------------------------------------------------------------=
29+
30+
@inlinable init(_ components: Components) {
31+
self.components = components
32+
self.attributes = Attributes(components)
33+
self.translator = Translator(components)
34+
}
35+
36+
//=------------------------------------------------------------------------=
37+
// MARK: Utilities
38+
//=------------------------------------------------------------------------=
39+
40+
@inlinable func number<T>(_ proposal: Proposal, as kind: T.Type)
41+
throws -> Number? where T: NumberTextKind {
42+
//=--------------------------------------=
43+
// Proposal
44+
//=--------------------------------------=
45+
var proposal = proposal
46+
translator.translateSingleSymbol(in: &proposal)
47+
let sign = components.consumeSingleSign(in: &proposal)
48+
//=--------------------------------------=
49+
// Number
50+
//=--------------------------------------=
51+
guard var number = try Number(
52+
in: proposal.merged(),
53+
using: components, as: T.self)
54+
else { return nil }
55+
56+
if let sign = sign { number.sign = sign }
57+
//=--------------------------------------=
58+
// Return
59+
//=--------------------------------------=
60+
return number
61+
}
62+
}

0 commit comments

Comments
 (0)