Skip to content

Commit 3445371

Browse files
authored
Flatten the ArgumentSet storage into a single array (#235)
This removes the nesting inside the ArgumentSet data structure, which had semantic meaning in an earlier version. This flattening, plus a switch to using dictionary lookup instead of linear scanning, provides another performance boost.
1 parent db24cb1 commit 3445371

File tree

10 files changed

+42
-165
lines changed

10 files changed

+42
-165
lines changed

Sources/ArgumentParser/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ add_library(ArgumentParser
2323
Parsing/ArgumentDecoder.swift
2424
Parsing/ArgumentDefinition.swift
2525
Parsing/ArgumentSet.swift
26-
Parsing/ArgumentSetSequence.swift
2726
Parsing/CommandParser.swift
2827
Parsing/InputOrigin.swift
2928
Parsing/Name.swift

Sources/ArgumentParser/Parsable Properties/Argument.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,8 @@ extension Argument {
280280
values.set(v, forKey: key, inputOrigin: origin)
281281
}
282282
})
283-
return ArgumentSet(alternatives: [arg])
284-
})
283+
return ArgumentSet(arg)
284+
})
285285
}
286286

287287
/// Creates a property that reads its value from an argument, parsing with
@@ -403,7 +403,7 @@ extension Argument {
403403
update: .appendToArray(forType: Element.self, key: key),
404404
initial: setInitialValue)
405405
arg.help.defaultValue = helpDefaultValue
406-
return ArgumentSet(alternatives: [arg])
406+
return ArgumentSet(arg)
407407
})
408408
}
409409

@@ -501,7 +501,7 @@ extension Argument {
501501
}),
502502
initial: setInitialValue)
503503
arg.help.defaultValue = helpDefaultValue
504-
return ArgumentSet(alternatives: [arg])
504+
return ArgumentSet(arg)
505505
})
506506
}
507507

Sources/ArgumentParser/Parsable Properties/Flag.swift

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,7 @@ extension Flag where Value: EnumerableFlag {
400400
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
401401
}))
402402
}
403-
return exclusivity == .exclusive
404-
? ArgumentSet(exclusive: args)
405-
: ArgumentSet(additive: args)
403+
return ArgumentSet(args)
406404
})
407405
}
408406

@@ -525,9 +523,7 @@ extension Flag {
525523
}))
526524

527525
}
528-
return exclusivity == .exclusive
529-
? ArgumentSet(exclusive: args)
530-
: ArgumentSet(additive: args)
526+
return ArgumentSet(args)
531527
})
532528
}
533529

@@ -553,7 +549,7 @@ extension Flag {
553549
})
554550
}))
555551
}
556-
return ArgumentSet(additive: args)
552+
return ArgumentSet(args)
557553
})
558554
}
559555

@@ -628,9 +624,7 @@ extension Flag where Value: CaseIterable, Value: RawRepresentable, Value: Equata
628624
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
629625
}))
630626
}
631-
return exclusivity == .exclusive
632-
? ArgumentSet(exclusive: args)
633-
: ArgumentSet(additive: args)
627+
return ArgumentSet(args)
634628
})
635629
}
636630
}
@@ -656,9 +650,7 @@ extension Flag {
656650
hasUpdated = try ArgumentSet.updateFlag(key: key, value: value, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
657651
}))
658652
}
659-
return exclusivity == .exclusive
660-
? ArgumentSet(exclusive: args)
661-
: ArgumentSet(additive: args)
653+
return ArgumentSet(args)
662654
})
663655
}
664656

@@ -686,7 +678,7 @@ extension Flag {
686678
})
687679
}))
688680
}
689-
return ArgumentSet(additive: args)
681+
return ArgumentSet(args)
690682
})
691683
}
692684
}

Sources/ArgumentParser/Parsable Properties/Option.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ extension Option {
391391
})
392392
arg.help.options.formUnion(ArgumentDefinition.Help.Options(type: Value.self))
393393
arg.help.defaultValue = initial.map { "\($0)" }
394-
return ArgumentSet(alternatives: [arg])
394+
return ArgumentSet(arg)
395395
})
396396
}
397397

@@ -534,7 +534,7 @@ extension Option {
534534
initial: setInitialValue
535535
)
536536
arg.help.defaultValue = helpDefaultValue
537-
return ArgumentSet(alternatives: [arg])
537+
return ArgumentSet(arg)
538538
})
539539
}
540540

@@ -636,7 +636,7 @@ extension Option {
636636
initial: setInitialValue
637637
)
638638
arg.help.defaultValue = helpDefaultValue
639-
return ArgumentSet(alternatives: [arg])
639+
return ArgumentSet(arg)
640640
})
641641
}
642642

Sources/ArgumentParser/Parsable Types/ParsableArguments.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ extension ArgumentSet {
237237
let key = InputKey(rawValue: codingKey)
238238
return parsed.argumentSet(for: key)
239239
}
240-
self.init(additive: a)
240+
self.init(sets: a)
241241
}
242242
}
243243

Sources/ArgumentParser/Parsable Types/ParsableArgumentsValidation.swift

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,11 @@ extension ParsableArguments {
5555

5656
fileprivate extension ArgumentSet {
5757
var firstPositionalArgument: ArgumentDefinition? {
58-
switch content {
59-
case .arguments(let arguments):
60-
return arguments.first(where: { $0.isPositional })
61-
case .sets(let sets):
62-
return sets.first(where: { $0.firstPositionalArgument != nil })?.firstPositionalArgument
63-
}
58+
content.first(where: { $0.isPositional })
6459
}
6560

6661
var firstRepeatedPositionalArgument: ArgumentDefinition? {
67-
switch content {
68-
case .arguments(let arguments):
69-
return arguments.first(where: { $0.isRepeatingPositional })
70-
case .sets(let sets):
71-
return sets.first(where: { $0.firstRepeatedPositionalArgument != nil })?.firstRepeatedPositionalArgument
72-
}
62+
content.first(where: { $0.isRepeatingPositional })
7363
}
7464
}
7565

@@ -234,13 +224,8 @@ struct ParsableArgumentsUniqueNamesValidator: ParsableArgumentsValidator {
234224
}
235225

236226
let countedNames: [String: Int] = argSets.reduce(into: [:]) { countedNames, args in
237-
switch args.content {
238-
case .arguments(let defs):
239-
for name in defs.flatMap({ $0.names }) {
240-
countedNames[name.synopsisString, default: 0] += 1
241-
}
242-
default:
243-
break
227+
for name in args.content.flatMap({ $0.names }) {
228+
countedNames[name.synopsisString, default: 0] += 1
244229
}
245230
}
246231

Sources/ArgumentParser/Parsing/ArgumentSet.swift

Lines changed: 22 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,74 +19,38 @@
1919
/// The `-v | -f` part is one *set* that’s optional, `<input> <output>` is
2020
/// another. Both of these can then be combined into a third set.
2121
struct ArgumentSet {
22-
enum Content {
23-
/// A leaf list of arguments.
24-
case arguments([ArgumentDefinition])
25-
/// A node with additional `[ArgumentSet]`
26-
case sets([ArgumentSet])
27-
}
28-
var content: Content
29-
var kind: Kind
22+
var content: [ArgumentDefinition] = []
23+
var namePositions: [Name: Int] = [:]
3024

31-
/// Used to generate _help_ text.
32-
enum Kind {
33-
/// Independent
34-
case additive
35-
/// Mutually exclusive
36-
case exclusive
37-
/// Several ways of achieving the same behavior. Should only display one.
38-
case alternatives
25+
init<S: Sequence>(_ arguments: S) where S.Element == ArgumentDefinition {
26+
self.content = Array(arguments)
27+
self.namePositions = Dictionary(
28+
content.enumerated().flatMap { i, arg in arg.names.map { ($0, i) } },
29+
uniquingKeysWith: { first, _ in first })
3930
}
4031

41-
init(arguments: [ArgumentDefinition], kind: Kind) {
42-
self.content = .arguments(arguments)
43-
self.kind = kind
44-
}
32+
init() {}
4533

46-
init(sets: [ArgumentSet], kind: Kind) {
47-
self.content = .sets(sets)
48-
self.kind = kind
34+
init(_ arg: ArgumentDefinition) {
35+
self.init([arg])
36+
}
37+
38+
init(sets: [ArgumentSet]) {
39+
self.init(sets.joined())
4940
}
5041
}
5142

5243
extension ArgumentSet: CustomDebugStringConvertible {
5344
var debugDescription: String {
54-
switch content {
55-
case .arguments(let args):
56-
return args
57-
.map { $0.debugDescription }
58-
.joined(separator: " / ")
59-
case .sets(let sets):
60-
return sets
61-
.map { "{\($0.debugDescription)}" }
62-
.joined(separator: " / ")
63-
}
45+
content
46+
.map { $0.debugDescription }
47+
.joined(separator: " / ")
6448
}
6549
}
6650

67-
extension ArgumentSet {
68-
init() {
69-
self.init(arguments: [], kind: .additive)
70-
}
71-
72-
init(_ arg: ArgumentDefinition) {
73-
self.init(arguments: [arg], kind: .additive)
74-
}
75-
76-
init(additive args: [ArgumentDefinition]) {
77-
self.init(arguments: args, kind: .additive)
78-
}
79-
80-
init(exclusive args: [ArgumentDefinition]) {
81-
self.init(arguments: args, kind: args.count == 1 ? .additive : .exclusive)
82-
}
83-
84-
init(alternatives args: [ArgumentDefinition]) {
85-
self.init(arguments: args, kind: args.count == 1 ? .additive : .alternatives)
86-
}
87-
88-
init(additive sets: [ArgumentSet]) {
89-
self.init(sets: sets, kind: .additive)
51+
extension ArgumentSet: Sequence {
52+
func makeIterator() -> Array<ArgumentDefinition>.Iterator {
53+
return content.makeIterator()
9054
}
9155
}
9256

@@ -150,7 +114,7 @@ extension ArgumentSet {
150114
let disableArg = ArgumentDefinition(kind: .named(disableNames), help: ArgumentDefinition.Help(options: [.isOptional], key: key), completion: .default, update: .nullary({ (origin, name, values) in
151115
hasUpdated = try ArgumentSet.updateFlag(key: key, value: false, origin: origin, values: &values, hasUpdated: hasUpdated, exclusivity: exclusivity)
152116
}), initial: { _, _ in })
153-
return ArgumentSet(exclusive: [enableArg, disableArg])
117+
return ArgumentSet([enableArg, disableArg])
154118
}
155119

156120
/// Creates an argument set for an incrementing integer flag.
@@ -390,7 +354,7 @@ extension ArgumentSet {
390354
func first(
391355
matching parsed: ParsedArgument
392356
) -> ArgumentDefinition? {
393-
return first(where: { $0.names.contains(parsed.name) })
357+
namePositions[parsed.name].map { content[$0] }
394358
}
395359

396360
func firstPositional(

Sources/ArgumentParser/Parsing/ArgumentSetSequence.swift

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

Sources/ArgumentParser/Parsing/Name.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12-
enum Name: Equatable {
12+
enum Name: Hashable {
1313
/// A name (usually multi-character) prefixed with `--` (2 dashes) or equivalent.
1414
case long(String)
1515
/// A single character name prefixed with `-` (1 dash) or equivalent.

Sources/ArgumentParser/Usage/UsageGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension UsageGenerator {
2727
}
2828

2929
init(toolName: String, definition: [ArgumentSet]) {
30-
self.init(toolName: toolName, definition: ArgumentSet(additive: definition))
30+
self.init(toolName: toolName, definition: ArgumentSet(sets: definition))
3131
}
3232
}
3333

0 commit comments

Comments
 (0)