diff --git a/Sources/XMLCoder/Auxiliaries/XMLElement.swift b/Sources/XMLCoder/Auxiliaries/XMLElement.swift index f535d210..734eb1f0 100644 --- a/Sources/XMLCoder/Auxiliaries/XMLElement.swift +++ b/Sources/XMLCoder/Auxiliaries/XMLElement.swift @@ -43,10 +43,18 @@ struct _XMLElement { elements = Dictionary(uniqueKeysWithValues: box.elements.map { key, box in switch box { + case let sharedUnkeyedBox as SharedBox: + let box = sharedUnkeyedBox.unbox() as! UnkeyedBox + let elements = box.map { _XMLElement(key: key, box: $0) } + return (key, elements) case let unkeyedBox as UnkeyedBox: // This basically injects the unkeyed children directly into self: let elements = unkeyedBox.map { _XMLElement(key: key, box: $0) } return (key, elements) + case let sharedKeyedBox as SharedBox: + let box = sharedKeyedBox.unbox() as! KeyedBox + let elements = [_XMLElement(key: key, box: box)] + return (key, elements) case let keyedBox as KeyedBox: let elements = [_XMLElement(key: key, box: keyedBox)] return (key, elements) @@ -66,6 +74,10 @@ struct _XMLElement { init(key: String, box: Box) { switch box { + case let sharedUnkeyedBox as SharedBox: + self.init(key: key, box: sharedUnkeyedBox.unbox()) + case let sharedKeyedBox as SharedBox: + self.init(key: key, box: sharedKeyedBox.unbox()) case let unkeyedBox as UnkeyedBox: self.init(key: key, box: unkeyedBox) case let keyedBox as KeyedBox: @@ -93,36 +105,41 @@ struct _XMLElement { var elements: [String: Box] = [:] for (key, value) in self.elements { for child in value { - if let content = child.value { - switch elements[key] { - case let unkeyedBox as UnkeyedBox: - unkeyedBox.append(StringBox(content)) - elements[key] = unkeyedBox - case let keyedBox as StringBox: - elements[key] = UnkeyedBox([keyedBox, StringBox(content)]) - default: - elements[key] = StringBox(content) + let hasValue = child.value != nil + let hasElements = !child.elements.isEmpty + let hasAttributes = !child.attributes.isEmpty + + if hasValue || hasElements || hasAttributes { + if let content = child.value { + switch elements[key] { + case var unkeyedBox as UnkeyedBox: + unkeyedBox.append(StringBox(content)) + elements[key] = unkeyedBox + case let stringBox as StringBox: + elements[key] = UnkeyedBox([stringBox, StringBox(content)]) + default: + elements[key] = StringBox(content) + } } - } else if !child.elements.isEmpty || !child.attributes.isEmpty { - let content = child.flatten() - if let existingValue = elements[key] { - if let unkeyedBox = existingValue as? UnkeyedBox { - var boxes = unkeyedBox.unbox() - boxes.append(content) - elements[key] = UnkeyedBox(boxes) - } else { - elements[key] = UnkeyedBox([existingValue, content]) + if hasElements || hasAttributes { + let content = child.flatten() + switch elements[key] { + case var unkeyedBox as UnkeyedBox: + unkeyedBox.append(content) + elements[key] = unkeyedBox + case let box?: + elements[key] = UnkeyedBox([box, content]) + default: + elements[key] = content } - } else { - elements[key] = content } } else { switch elements[key] { - case let unkeyedBox as UnkeyedBox: + case var unkeyedBox as UnkeyedBox: unkeyedBox.append(NullBox()) elements[key] = unkeyedBox - case let keyedBox as StringBox: - elements[key] = UnkeyedBox([keyedBox, NullBox()]) + case let box?: + elements[key] = UnkeyedBox([box, NullBox()]) default: elements[key] = NullBox() } @@ -130,7 +147,9 @@ struct _XMLElement { } } - return KeyedBox(elements: elements, attributes: attributes) + let keyedBox = KeyedBox(elements: elements, attributes: attributes) + + return keyedBox } func toXMLString(with header: XMLHeader? = nil, withCDATA cdata: Bool, formatting: XMLEncoder.OutputFormatting, ignoreEscaping _: Bool = false) -> String { diff --git a/Sources/XMLCoder/Box/Box.swift b/Sources/XMLCoder/Box/Box.swift index 70ce4a8a..8dfdb829 100644 --- a/Sources/XMLCoder/Box/Box.swift +++ b/Sources/XMLCoder/Box/Box.swift @@ -13,4 +13,10 @@ protocol Box { } /// A box that only describes a single atomic value. -protocol SimpleBox: Box {} +protocol SimpleBox: Box { + // A simple tagging protocol, for now. +} + +protocol SharedBoxProtocol { + func unbox() -> Box +} diff --git a/Sources/XMLCoder/Box/KeyedBox.swift b/Sources/XMLCoder/Box/KeyedBox.swift index b9743b76..3a94f6dd 100644 --- a/Sources/XMLCoder/Box/KeyedBox.swift +++ b/Sources/XMLCoder/Box/KeyedBox.swift @@ -62,7 +62,7 @@ extension KeyedStorage: CustomStringConvertible { } } -class KeyedBox { +struct KeyedBox { typealias Key = String typealias Attribute = SimpleBox typealias Element = Box diff --git a/Sources/XMLCoder/Box/SharedBox.swift b/Sources/XMLCoder/Box/SharedBox.swift new file mode 100644 index 00000000..8106a15c --- /dev/null +++ b/Sources/XMLCoder/Box/SharedBox.swift @@ -0,0 +1,36 @@ +// +// SharedBox.swift +// XMLCoder +// +// Created by Vincent Esche on 12/22/18. +// + +import Foundation + +class SharedBox { + fileprivate var unboxed: Unboxed + + init(_ wrapped: Unboxed) { + unboxed = wrapped + } + + func withShared(_ body: (inout Unboxed) throws -> Result) rethrows -> Result { + return try body(&unboxed) + } +} + +extension SharedBox: Box { + var isNull: Bool { + return unboxed.isNull + } + + func xmlString() -> String? { + return unboxed.xmlString() + } +} + +extension SharedBox: SharedBoxProtocol { + func unbox() -> Box { + return unboxed + } +} diff --git a/Sources/XMLCoder/Box/UnkeyedBox.swift b/Sources/XMLCoder/Box/UnkeyedBox.swift index b931934d..f5862822 100644 --- a/Sources/XMLCoder/Box/UnkeyedBox.swift +++ b/Sources/XMLCoder/Box/UnkeyedBox.swift @@ -8,7 +8,7 @@ import Foundation // Minimalist implementation of an order-preserving unkeyed box: -class UnkeyedBox { +struct UnkeyedBox { typealias Element = Box typealias Unboxed = [Element] @@ -35,11 +35,11 @@ class UnkeyedBox { return unboxed } - func append(_ newElement: Element) { + mutating func append(_ newElement: Element) { unboxed.append(newElement) } - func insert(_ newElement: Element, at index: Int) { + mutating func insert(_ newElement: Element, at index: Int) { unboxed.insert(newElement, at: index) } } diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index c6e83262..a3ff939c 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -332,7 +332,7 @@ class _XMLDecoder: Decoder { )) } - guard let keyed = topContainer as? KeyedBox else { + guard let keyed = topContainer as? SharedBox else { throw DecodingError._typeMismatch(at: codingPath, expectation: [String: Any].self, reality: topContainer) } @@ -350,7 +350,7 @@ class _XMLDecoder: Decoder { )) } - let unkeyed = (topContainer as? UnkeyedBox) ?? UnkeyedBox([topContainer]) + let unkeyed = (topContainer as? SharedBox) ?? SharedBox(UnkeyedBox([topContainer])) return _XMLUnkeyedDecodingContainer(referencing: self, wrapping: unkeyed) } diff --git a/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift b/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift index 839cfb07..69fde464 100644 --- a/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift +++ b/Sources/XMLCoder/Decoder/XMLDecodingStorage.swift @@ -33,7 +33,13 @@ struct _XMLDecodingStorage { } mutating func push(container: Box) { - containers.append(container) + if let keyedBox = container as? KeyedBox { + containers.append(SharedBox(keyedBox)) + } else if let unkeyedBox = container as? UnkeyedBox { + containers.append(SharedBox(unkeyedBox)) + } else { + containers.append(container) + } } @discardableResult diff --git a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift index bc3069a1..25410793 100644 --- a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift @@ -12,6 +12,8 @@ import Foundation struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol { typealias Key = K + typealias KeyedContainer = SharedBox + typealias UnkeyedContainer = SharedBox // MARK: Properties @@ -19,7 +21,7 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol private let decoder: _XMLDecoder /// A reference to the container we're reading from. - private let container: KeyedBox + private let container: KeyedContainer /// The path of coding keys taken to get to this point in decoding. public private(set) var codingPath: [CodingKey] @@ -27,8 +29,20 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol // MARK: - Initialization /// Initializes `self` by referencing the given decoder and container. - init(referencing decoder: _XMLDecoder, wrapping container: KeyedBox) { + init(referencing decoder: _XMLDecoder, wrapping container: KeyedContainer) { self.decoder = decoder + + func mapKeys(_ container: KeyedContainer, closure: (String) -> String) -> KeyedContainer { + let attributes = container.withShared { keyedBox in + keyedBox.attributes.map { (closure($0), $1) } + } + let elements = container.withShared { keyedBox in + keyedBox.elements.map { (closure($0), $1) } + } + let keyedBox = KeyedBox(elements: elements, attributes: attributes) + return SharedBox(keyedBox) + } + switch decoder.options.keyDecodingStrategy { case .useDefaultKeys: self.container = container @@ -36,33 +50,18 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol // Convert the snake case keys in the container to camel case. // If we hit a duplicate key after conversion, then we'll use the // first one we saw. Effectively an undefined behavior with dictionaries. - let attributes = container.attributes.map { key, value in - (XMLDecoder.KeyDecodingStrategy._convertFromSnakeCase(key), value) - } - let elements = container.elements.map { key, value in - (XMLDecoder.KeyDecodingStrategy._convertFromSnakeCase(key), value) + self.container = mapKeys(container) { key in + XMLDecoder.KeyDecodingStrategy._convertFromSnakeCase(key) } - self.container = KeyedBox(elements: elements, attributes: attributes) case .convertFromCapitalized: - let attributes = container.attributes.map { key, value in - (XMLDecoder.KeyDecodingStrategy._convertFromCapitalized(key), value) + self.container = mapKeys(container) { key in + XMLDecoder.KeyDecodingStrategy._convertFromCapitalized(key) } - let elements = container.elements.map { key, value in - (XMLDecoder.KeyDecodingStrategy._convertFromCapitalized(key), value) - } - self.container = KeyedBox(elements: elements, attributes: attributes) case let .custom(converter): - let attributes = container.attributes.map { key, value in - (converter(decoder.codingPath + - [_XMLKey(stringValue: key, intValue: nil)]).stringValue, - value) - } - let elements = container.elements.map { key, value in - (converter(decoder.codingPath + - [_XMLKey(stringValue: key, intValue: nil)]).stringValue, - value) + self.container = mapKeys(container) { key in + let codingPath = decoder.codingPath + [_XMLKey(stringValue: key, intValue: nil)] + return converter(codingPath).stringValue } - self.container = KeyedBox(elements: elements, attributes: attributes) } codingPath = decoder.codingPath } @@ -70,14 +69,27 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol // MARK: - KeyedDecodingContainerProtocol Methods public var allKeys: [Key] { - let attributeKeys = Array(container.attributes.keys.compactMap { Key(stringValue: $0) }) - let elementKeys = Array(container.elements.keys.compactMap { Key(stringValue: $0) }) + let elementKeys = container.withShared { keyedBox in + keyedBox.elements.keys.compactMap { Key(stringValue: $0) } + } + + let attributeKeys = container.withShared { keyedBox in + keyedBox.attributes.keys.compactMap { Key(stringValue: $0) } + } + return attributeKeys + elementKeys } public func contains(_ key: Key) -> Bool { - let keyString = key.stringValue - return (container.attributes[keyString] != nil) || (container.elements[keyString] != nil) + let elementOrNil = container.withShared { keyedBox in + keyedBox.elements[key.stringValue] + } + + let attributeOrNil = container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] + } + + return (elementOrNil ?? attributeOrNil) != nil } private func _errorDescription(of key: CodingKey) -> String { @@ -98,14 +110,17 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } public func decodeNil(forKey key: Key) throws -> Bool { - let keyString = key.stringValue - if let entry = container.attributes[keyString] { - return entry.isNull - } else if let entry = container.elements[key.stringValue] { - return entry.isNull - } else { - return true + let elementOrNil = container.withShared { keyedBox in + keyedBox.elements[key.stringValue] + } + + let attributeOrNil = container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] } + + let box = elementOrNil ?? attributeOrNil + + return box?.isNull ?? true } public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { @@ -177,11 +192,14 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol } public func decode(_ type: T.Type, forKey key: Key) throws -> T { - let attributeNotFound = (container.attributes[key.stringValue] == nil) - let elementNotFound = (container.elements[key.stringValue] == nil) + let attributeNotFound = container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] == nil + } + let elementNotFound = container.withShared { keyedBox in + keyedBox.elements[key.stringValue] == nil + } - if let type = type as? AnyEmptySequence.Type, - attributeNotFound, elementNotFound { + if let type = type as? AnyEmptySequence.Type, attributeNotFound, elementNotFound { return type.init() as! T } @@ -210,7 +228,15 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol _ type: T.Type, forKey key: Key ) throws -> T { - guard let entry = container.elements[key.stringValue] ?? container.attributes[key.stringValue] else { + let elementOrNil = container.withShared { keyedBox in + keyedBox.elements[key.stringValue] + } + + let attributeOrNil = container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] + } + + guard let entry = elementOrNil ?? attributeOrNil else { throw DecodingError.keyNotFound(key, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "No value associated with key \(_errorDescription(of: key))." @@ -243,22 +269,46 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol return unwrapped } - public func nestedContainer(keyedBy _: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer { + public func nestedContainer( + keyedBy _: NestedKey.Type, forKey key: Key + ) throws -> KeyedDecodingContainer { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } - guard let value = self.container.elements[key.stringValue] ?? self.container.attributes[key.stringValue] else { + let elementOrNil = self.container.withShared { keyedBox in + keyedBox.elements[key.stringValue] + } + + let attributeOrNil = self.container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] + } + + guard let value = elementOrNil ?? attributeOrNil else { throw DecodingError.keyNotFound(key, DecodingError.Context( codingPath: codingPath, debugDescription: "Cannot get \(KeyedDecodingContainer.self) -- no value found for key \"\(key.stringValue)\"" )) } - guard let keyed = value as? KeyedBox else { - throw DecodingError._typeMismatch(at: codingPath, expectation: [String: Any].self, reality: value) + let container: _XMLKeyedDecodingContainer + if let keyedContainer = value as? KeyedContainer { + container = _XMLKeyedDecodingContainer( + referencing: decoder, + wrapping: keyedContainer + ) + } else if let keyedContainer = value as? KeyedBox { + container = _XMLKeyedDecodingContainer( + referencing: decoder, + wrapping: SharedBox(keyedContainer) + ) + } else { + throw DecodingError._typeMismatch( + at: codingPath, + expectation: [String: Any].self, + reality: value + ) } - let container = _XMLKeyedDecodingContainer(referencing: decoder, wrapping: keyed) return KeyedDecodingContainer(container) } @@ -266,25 +316,43 @@ struct _XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } - guard let value = container.elements[key.stringValue] ?? container.attributes[key.stringValue] else { + let elementOrNil = container.withShared { keyedBox in + keyedBox.elements[key.stringValue] + } + + let attributeOrNil = container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] + } + + guard let value = elementOrNil ?? attributeOrNil else { throw DecodingError.keyNotFound(key, DecodingError.Context( codingPath: codingPath, debugDescription: "Cannot get UnkeyedDecodingContainer -- no value found for key \"\(key.stringValue)\"" )) } - guard let unkeyed = value as? UnkeyedBox else { + if let unkeyedContainer = value as? UnkeyedContainer { + return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyedContainer) + } else if let unkeyedContainer = value as? UnkeyedBox { + return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: SharedBox(unkeyedContainer)) + } else { throw DecodingError._typeMismatch(at: codingPath, expectation: [Any].self, reality: value) } - - return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyed) } private func _superDecoder(forKey key: CodingKey) throws -> Decoder { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } - let box: Box = container.elements[key.stringValue] ?? container.attributes[key.stringValue] ?? NullBox() + let elementOrNil = container.withShared { keyedBox in + keyedBox.elements[key.stringValue] + } + + let attributeOrNil = container.withShared { keyedBox in + keyedBox.attributes[key.stringValue] + } + + let box: Box = elementOrNil ?? attributeOrNil ?? NullBox() return _XMLDecoder(referencing: box, at: decoder.codingPath, options: decoder.options) } diff --git a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift index adf42f62..c8ecffb3 100644 --- a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift @@ -9,13 +9,16 @@ import Foundation struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { + typealias KeyedContainer = SharedBox + typealias UnkeyedContainer = SharedBox + // MARK: Properties /// A reference to the decoder we're reading from. private let decoder: _XMLDecoder /// A reference to the container we're reading from. - private let container: UnkeyedBox + private let container: UnkeyedContainer /// The path of coding keys taken to get to this point in decoding. public private(set) var codingPath: [CodingKey] @@ -26,7 +29,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { // MARK: - Initialization /// Initializes `self` by referencing the given decoder and container. - init(referencing decoder: _XMLDecoder, wrapping container: UnkeyedBox) { + init(referencing decoder: _XMLDecoder, wrapping container: UnkeyedContainer) { self.decoder = decoder self.container = container codingPath = decoder.codingPath @@ -36,7 +39,9 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { // MARK: - UnkeyedDecodingContainer Methods public var count: Int? { - return container.count + return container.withShared { unkeyedBox in + unkeyedBox.count + } } public var isAtEnd: Bool { @@ -51,7 +56,11 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { )) } - if container[self.currentIndex].isNull { + let isNull = container.withShared { unkeyedBox in + unkeyedBox[self.currentIndex].isNull + } + + if isNull { currentIndex += 1 return true } else { @@ -164,14 +173,17 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { // work around unkeyed box wrapped as single element of keyed box if let type = type as? AnyArray.Type, - let keyedBox = container[self.currentIndex] as? KeyedBox, + let keyedBox = container + .withShared({ $0[self.currentIndex] as? KeyedBox }), keyedBox.attributes.count == 0, keyedBox.elements.count == 1, let firstKey = keyedBox.elements.keys.first, let unkeyedBox = keyedBox.elements[firstKey] { box = unkeyedBox } else { - box = container[self.currentIndex] + box = container.withShared { unkeyedBox in + unkeyedBox[self.currentIndex] + } } let value = try decode(decoder, box) @@ -207,7 +219,9 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { ) } - let value = self.container[self.currentIndex] + let value = self.container.withShared { unkeyedBox in + unkeyedBox[self.currentIndex] + } guard !value.isNull else { throw DecodingError.valueNotFound(KeyedDecodingContainer.self, DecodingError.Context( codingPath: codingPath, @@ -215,7 +229,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { )) } - guard let keyed = value as? KeyedBox else { + guard let keyedContainer = value as? KeyedContainer else { throw DecodingError._typeMismatch(at: codingPath, expectation: [String: Any].self, reality: value) @@ -224,7 +238,7 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { currentIndex += 1 let container = _XMLKeyedDecodingContainer( referencing: decoder, - wrapping: keyed + wrapping: keyedContainer ) return KeyedDecodingContainer(container) } @@ -242,7 +256,9 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { ) } - let value = container[self.currentIndex] + let value = container.withShared { unkeyedBox in + unkeyedBox[self.currentIndex] + } guard !value.isNull else { throw DecodingError.valueNotFound(UnkeyedDecodingContainer.self, DecodingError.Context( codingPath: codingPath, @@ -250,14 +266,14 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { )) } - guard let unkeyed = value as? UnkeyedBox else { + guard let unkeyedContainer = value as? UnkeyedContainer else { throw DecodingError._typeMismatch(at: codingPath, expectation: UnkeyedBox.self, reality: value) } currentIndex += 1 - return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyed) + return _XMLUnkeyedDecodingContainer(referencing: decoder, wrapping: unkeyedContainer) } public mutating func superDecoder() throws -> Decoder { @@ -271,7 +287,9 @@ struct _XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { )) } - let value = container[self.currentIndex] + let value = container.withShared { unkeyedBox in + unkeyedBox[self.currentIndex] + } currentIndex += 1 return _XMLDecoder(referencing: value, at: decoder.codingPath, diff --git a/Sources/XMLCoder/Encoder/XMLEncoder.swift b/Sources/XMLCoder/Encoder/XMLEncoder.swift index 211acb3a..64787f3b 100644 --- a/Sources/XMLCoder/Encoder/XMLEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLEncoder.swift @@ -270,12 +270,12 @@ open class XMLEncoder { let elementOrNone: _XMLElement? - if let keyed = topLevel as? KeyedBox { - elementOrNone = _XMLElement(key: rootKey, box: keyed) - } else if let unkeyed = topLevel as? UnkeyedBox { - elementOrNone = _XMLElement(key: rootKey, box: unkeyed) + if let keyedBox = topLevel as? KeyedBox { + elementOrNone = _XMLElement(key: rootKey, box: keyedBox) + } else if let unkeyedBox = topLevel as? UnkeyedBox { + elementOrNone = _XMLElement(key: rootKey, box: unkeyedBox) } else { - fatalError("Unrecognized top-level element.") + fatalError("Unrecognized top-level element of type: \(type(of: topLevel))") } guard let element = elementOrNone else { @@ -340,12 +340,12 @@ class _XMLEncoder: Encoder { public func container(keyedBy _: Key.Type) -> KeyedEncodingContainer { // If an existing keyed container was already requested, return that one. - let topContainer: KeyedBox + let topContainer: SharedBox if canEncodeNewValue { // We haven't yet pushed a container at this level; do so here. topContainer = storage.pushKeyedContainer() } else { - guard let container = storage.lastContainer as? KeyedBox else { + guard let container = storage.lastContainer as? SharedBox else { preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.") } @@ -358,12 +358,12 @@ class _XMLEncoder: Encoder { public func unkeyedContainer() -> UnkeyedEncodingContainer { // If an existing unkeyed container was already requested, return that one. - let topContainer: UnkeyedBox + let topContainer: SharedBox if canEncodeNewValue { // We haven't yet pushed a container at this level; do so here. topContainer = storage.pushUnkeyedContainer() } else { - guard let container = storage.lastContainer as? UnkeyedBox else { + guard let container = storage.lastContainer as? SharedBox else { preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.") } @@ -558,13 +558,13 @@ extension _XMLEncoder { internal func box(_ value: T) throws -> Box { if T.self == Date.self || T.self == NSDate.self { - return try box(value as! Date) + return try self.box(value as! Date) } else if T.self == Data.self || T.self == NSData.self { - return try box(value as! Data) + return try self.box(value as! Data) } else if T.self == URL.self || T.self == NSURL.self { - return box(value as! URL) + return self.box(value as! URL) } else if T.self == Decimal.self || T.self == NSDecimalNumber.self { - return box(value as! Decimal) + return self.box(value as! Decimal) } let depth = storage.count @@ -575,6 +575,12 @@ extension _XMLEncoder { return KeyedBox() } - return storage.popContainer() + let box = storage.popContainer() + + guard let sharedBox = box as? SharedBoxProtocol else { + return box + } + + return sharedBox.unbox() } } diff --git a/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift b/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift index 1870ec12..78ec2a56 100644 --- a/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift +++ b/Sources/XMLCoder/Encoder/XMLEncodingStorage.swift @@ -32,20 +32,26 @@ struct _XMLEncodingStorage { return containers.last } - mutating func pushKeyedContainer() -> KeyedBox { - let keyed = KeyedBox() - containers.append(keyed) - return keyed + mutating func pushKeyedContainer() -> SharedBox { + let container = SharedBox(KeyedBox()) + containers.append(container) + return container } - mutating func pushUnkeyedContainer() -> UnkeyedBox { - let unkeyed = UnkeyedBox() - containers.append(unkeyed) - return unkeyed + mutating func pushUnkeyedContainer() -> SharedBox { + let container = SharedBox(UnkeyedBox()) + containers.append(container) + return container } mutating func push(container: Box) { - containers.append(container) + if let keyedBox = container as? KeyedBox { + containers.append(SharedBox(keyedBox)) + } else if let unkeyedBox = container as? UnkeyedBox { + containers.append(SharedBox(unkeyedBox)) + } else { + containers.append(container) + } } mutating func popContainer() -> Box { diff --git a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift index b0ab654f..4c0a806c 100644 --- a/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift +++ b/Sources/XMLCoder/Encoder/XMLKeyedEncodingContainer.swift @@ -16,7 +16,7 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol private let encoder: _XMLEncoder /// A reference to the container we're writing to. - private let container: KeyedBox + private var container: SharedBox /// The path of coding keys taken to get to this point in encoding. public private(set) var codingPath: [CodingKey] @@ -24,7 +24,7 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol // MARK: - Initialization /// Initializes `self` with the given references. - init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: KeyedBox) { + init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: SharedBox) { self.encoder = encoder self.codingPath = codingPath self.container = container @@ -47,7 +47,9 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol // MARK: - KeyedEncodingContainerProtocol Methods public mutating func encodeNil(forKey key: Key) throws { - container.elements[_converted(key).stringValue] = NullBox() + container.withShared { container in + container.elements[_converted(key).stringValue] = NullBox() + } } public mutating func encode(_ value: Bool, forKey key: Key) throws { @@ -179,30 +181,40 @@ struct _XMLKeyedEncodingContainer: KeyedEncodingContainerProtocol debugDescription: "Complex values cannot be encoded as attributes." )) } - container.attributes[_converted(key).stringValue] = attribute + container.withShared { container in + container.attributes[_converted(key).stringValue] = attribute + } case .element: - container.elements[_converted(key).stringValue] = box + container.withShared { container in + container.elements[_converted(key).stringValue] = box + } } } public mutating func nestedContainer(keyedBy _: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { - let keyed = KeyedBox() - self.container.elements[_converted(key).stringValue] = keyed + let sharedKeyed = SharedBox(KeyedBox()) + + self.container.withShared { container in + container.elements[_converted(key).stringValue] = sharedKeyed + } codingPath.append(key) defer { self.codingPath.removeLast() } - let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: keyed) + let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedKeyed) return KeyedEncodingContainer(container) } public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let unkeyed = UnkeyedBox() - container.elements[_converted(key).stringValue] = unkeyed + let sharedUnkeyed = SharedBox(UnkeyedBox()) + + container.withShared { container in + container.elements[_converted(key).stringValue] = sharedUnkeyed + } codingPath.append(key) defer { self.codingPath.removeLast() } - return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: unkeyed) + return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedUnkeyed) } public mutating func superEncoder() -> Encoder { diff --git a/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift b/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift index c73a72b8..096ac26d 100644 --- a/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift +++ b/Sources/XMLCoder/Encoder/XMLReferencingEncoder.swift @@ -18,10 +18,10 @@ class _XMLReferencingEncoder: _XMLEncoder { /// The type of container we're referencing. private enum Reference { /// Referencing a specific index in an unkeyed container. - case unkeyed(UnkeyedBox, Int) + case unkeyed(SharedBox, Int) /// Referencing a specific key in a keyed container. - case keyed(KeyedBox, String) + case keyed(SharedBox, String) } // MARK: - Properties @@ -38,10 +38,10 @@ class _XMLReferencingEncoder: _XMLEncoder { init( referencing encoder: _XMLEncoder, at index: Int, - wrapping unkeyed: UnkeyedBox + wrapping sharedUnkeyed: SharedBox ) { self.encoder = encoder - reference = .unkeyed(unkeyed, index) + reference = .unkeyed(sharedUnkeyed, index) super.init( options: encoder.options, nodeEncodings: encoder.nodeEncodings, @@ -56,10 +56,10 @@ class _XMLReferencingEncoder: _XMLEncoder { referencing encoder: _XMLEncoder, key: CodingKey, convertedKey: CodingKey, - wrapping keyed: KeyedBox + wrapping sharedKeyed: SharedBox ) { self.encoder = encoder - reference = .keyed(keyed, convertedKey.stringValue) + reference = .keyed(sharedKeyed, convertedKey.stringValue) super.init( options: encoder.options, nodeEncodings: encoder.nodeEncodings, @@ -90,10 +90,14 @@ class _XMLReferencingEncoder: _XMLEncoder { } switch self.reference { - case let .unkeyed(unkeyed, index): - unkeyed.insert(box, at: index) - case let .keyed(keyed, key): - keyed.elements[key] = box + case let .unkeyed(sharedUnkeyedBox, index): + sharedUnkeyedBox.withShared { unkeyedBox in + unkeyedBox.insert(box, at: index) + } + case let .keyed(sharedKeyedBox, key): + sharedKeyedBox.withShared { keyedBox in + keyedBox.elements[key] = box + } } } } diff --git a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift index fe5eeec6..1a088d8d 100644 --- a/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift +++ b/Sources/XMLCoder/Encoder/XMLUnkeyedEncodingContainer.swift @@ -14,20 +14,20 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { private let encoder: _XMLEncoder /// A reference to the container we're writing to. - private let container: UnkeyedBox + private let container: SharedBox /// The path of coding keys taken to get to this point in encoding. public private(set) var codingPath: [CodingKey] /// The number of elements encoded into the container. public var count: Int { - return container.count + return container.withShared { $0.count } } // MARK: - Initialization /// Initializes `self` with the given references. - init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: UnkeyedBox) { + init(referencing encoder: _XMLEncoder, codingPath: [CodingKey], wrapping container: SharedBox) { self.encoder = encoder self.codingPath = codingPath self.container = container @@ -36,7 +36,9 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { // MARK: - UnkeyedEncodingContainer Methods public mutating func encodeNil() throws { - container.append(encoder.box()) + container.withShared { container in + container.append(encoder.box()) + } } public mutating func encode(_ value: Bool) throws { @@ -147,17 +149,21 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { ) rethrows { encoder.codingPath.append(_XMLKey(index: count)) defer { self.encoder.codingPath.removeLast() } - container.append(try encode(encoder, value)) + try container.withShared { container in + container.append(try encode(encoder, value)) + } } public mutating func nestedContainer(keyedBy _: NestedKey.Type) -> KeyedEncodingContainer { codingPath.append(_XMLKey(index: count)) defer { self.codingPath.removeLast() } - let keyed = KeyedBox() - self.container.append(keyed) + let sharedKeyed = SharedBox(KeyedBox()) + self.container.withShared { container in + container.append(sharedKeyed) + } - let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: keyed) + let container = _XMLKeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedKeyed) return KeyedEncodingContainer(container) } @@ -165,13 +171,15 @@ struct _XMLUnkeyedEncodingContainer: UnkeyedEncodingContainer { codingPath.append(_XMLKey(index: count)) defer { self.codingPath.removeLast() } - let unkeyed = UnkeyedBox() - container.append(unkeyed) + let sharedUnkeyed = SharedBox(UnkeyedBox()) + container.withShared { container in + container.append(sharedUnkeyed) + } - return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: unkeyed) + return _XMLUnkeyedEncodingContainer(referencing: encoder, codingPath: codingPath, wrapping: sharedUnkeyed) } public mutating func superEncoder() -> Encoder { - return _XMLReferencingEncoder(referencing: encoder, at: container.count, wrapping: container) + return _XMLReferencingEncoder(referencing: encoder, at: count, wrapping: container) } } diff --git a/Tests/XMLCoderTests/Box/KeyedBoxTests.swift b/Tests/XMLCoderTests/Box/KeyedBoxTests.swift index 262c5646..ca5efb7d 100644 --- a/Tests/XMLCoderTests/Box/KeyedBoxTests.swift +++ b/Tests/XMLCoderTests/Box/KeyedBoxTests.swift @@ -53,7 +53,7 @@ class KeyedBoxTests: XCTestCase { } func testSubscript() { - let box = Boxed( + var box = Boxed( elements: ["foo": StringBox("bar"), "baz": IntBox(42)], attributes: ["baz": StringBox("blee")] ) diff --git a/Tests/XMLCoderTests/Box/SharedBoxTests.swift b/Tests/XMLCoderTests/Box/SharedBoxTests.swift new file mode 100644 index 00000000..c80c8248 --- /dev/null +++ b/Tests/XMLCoderTests/Box/SharedBoxTests.swift @@ -0,0 +1,63 @@ +// +// SharedBoxTests.swift +// XMLCoderTests +// +// Created by Vincent Esche on 12/26/18. +// + +import XCTest +@testable import XMLCoder + +class SharedBoxTests: XCTestCase { + func testInit() { + let box = SharedBox(BoolBox(false)) + box.withShared { shared in + XCTAssertFalse(shared.unbox()) + } + } + + func testIsNull() { + let box = SharedBox(BoolBox(false)) + XCTAssertEqual(box.isNull, false) + } + + func testXMLString() { + let nullBox = NullBox() + let sharedNullBox = SharedBox(nullBox) + XCTAssertEqual(sharedNullBox.xmlString(), nullBox.xmlString()) + + let boolBox = BoolBox(false) + let sharedBoolBox = SharedBox(boolBox) + XCTAssertEqual(sharedBoolBox.xmlString(), boolBox.xmlString()) + + let intBox = IntBox(42) + let sharedIntBox = SharedBox(intBox) + XCTAssertEqual(sharedIntBox.xmlString(), intBox.xmlString()) + + let stringBox = StringBox("lorem ipsum") + let sharedStringBox = SharedBox(stringBox) + XCTAssertEqual(sharedStringBox.xmlString(), stringBox.xmlString()) + } + + func testWithShared() { + let sharedBox = SharedBox(UnkeyedBox()) + let sharedBoxAlias = sharedBox + + XCTAssertEqual(sharedBox.withShared { $0.count }, 0) + XCTAssertEqual(sharedBoxAlias.withShared { $0.count }, 0) + + sharedBox.withShared { unkeyedBox in + unkeyedBox.append(NullBox()) + } + + XCTAssertEqual(sharedBox.withShared { $0.count }, 1) + XCTAssertEqual(sharedBoxAlias.withShared { $0.count }, 1) + + sharedBoxAlias.withShared { unkeyedBox in + unkeyedBox.append(NullBox()) + } + + XCTAssertEqual(sharedBox.withShared { $0.count }, 2) + XCTAssertEqual(sharedBoxAlias.withShared { $0.count }, 2) + } +} diff --git a/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift b/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift index 7daebf1b..16e50217 100644 --- a/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift +++ b/Tests/XMLCoderTests/Box/UnkeyedBoxTests.swift @@ -41,7 +41,7 @@ class UnkeyedBoxTests: XCTestCase { } func testSubscript() { - let box = Boxed([StringBox("foo"), IntBox(42)]) + var box = Boxed([StringBox("foo"), IntBox(42)]) box[0] = NullBox() XCTAssertEqual(box.count, 2) XCTAssertEqual(box[0] as? NullBox, NullBox()) @@ -49,7 +49,7 @@ class UnkeyedBoxTests: XCTestCase { } func testInsertAt() { - let box = Boxed([StringBox("foo"), IntBox(42)]) + var box = Boxed([StringBox("foo"), IntBox(42)]) box.insert(NullBox(), at: 1) XCTAssertEqual(box.count, 3) diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index edcd3010..4026101c 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -29,10 +29,12 @@ BF63EF0821CD7AF8001D38C5 /* URLBoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */; }; BF63EF0A21CD7C1A001D38C5 /* URLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0921CD7C1A001D38C5 /* URLTests.swift */; }; BF63EF0C21CD7F28001D38C5 /* EmptyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0B21CD7F28001D38C5 /* EmptyTests.swift */; }; + BF63EF1821CEB6BD001D38C5 /* SharedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF1721CEB6BD001D38C5 /* SharedBox.swift */; }; BF63EF1E21CEC99B001D38C5 /* BenchmarkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF1D21CEC99A001D38C5 /* BenchmarkTests.swift */; }; BF63EF6721D0F874001D38C5 /* XMLKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF6621D0F874001D38C5 /* XMLKeyTests.swift */; }; BF63EF6921D0FDB5001D38C5 /* XMLHeaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF6821D0FDB5001D38C5 /* XMLHeaderTests.swift */; }; BF63EF6B21D10284001D38C5 /* XMLElementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF6A21D10284001D38C5 /* XMLElementTests.swift */; }; + BF8171F221D3D03E00901EB0 /* SharedBoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8171F121D3D03E00901EB0 /* SharedBoxTests.swift */; }; BF9457A821CBB498005ACFDE /* NullBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF94579E21CBB497005ACFDE /* NullBox.swift */; }; BF9457A921CBB498005ACFDE /* KeyedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF94579F21CBB497005ACFDE /* KeyedBox.swift */; }; BF9457AA21CBB498005ACFDE /* UnkeyedBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457A021CBB497005ACFDE /* UnkeyedBox.swift */; }; @@ -73,6 +75,7 @@ BF9457F521CBB6BC005ACFDE /* DecimalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EA21CBB6BC005ACFDE /* DecimalTests.swift */; }; BF9457F621CBB6BC005ACFDE /* KeyedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EB21CBB6BC005ACFDE /* KeyedTests.swift */; }; BF9457F721CBB6BC005ACFDE /* DataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9457EC21CBB6BC005ACFDE /* DataTests.swift */; }; + D1CB1EF521EA9599009CAF02 /* RJITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* RJITest.swift */; }; D1E0C85321D8E65E0042A261 /* ErrorContextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C85121D8E6540042A261 /* ErrorContextTest.swift */; }; D1E0C85521D91EBF0042A261 /* Metatypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C85421D91EBF0042A261 /* Metatypes.swift */; }; D1FC040521C7EF8200065B43 /* RJISample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1FC040421C7EF8200065B43 /* RJISample.swift */; }; @@ -96,7 +99,6 @@ OBJ_86 /* NoteTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* NoteTest.swift */; }; OBJ_87 /* PlantCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_35 /* PlantCatalog.swift */; }; OBJ_88 /* PlantTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_36 /* PlantTest.swift */; }; - OBJ_89 /* RJITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* RJITest.swift */; }; OBJ_90 /* RelationshipsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* RelationshipsTest.swift */; }; OBJ_92 /* XMLCoder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "XMLCoder::XMLCoder::Product" /* XMLCoder.framework */; }; /* End PBXBuildFile section */ @@ -127,10 +129,12 @@ BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLBoxTests.swift; sourceTree = ""; }; BF63EF0921CD7C1A001D38C5 /* URLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLTests.swift; sourceTree = ""; }; BF63EF0B21CD7F28001D38C5 /* EmptyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTests.swift; sourceTree = ""; }; + BF63EF1721CEB6BD001D38C5 /* SharedBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedBox.swift; sourceTree = ""; }; BF63EF1D21CEC99A001D38C5 /* BenchmarkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BenchmarkTests.swift; sourceTree = ""; }; BF63EF6621D0F874001D38C5 /* XMLKeyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLKeyTests.swift; sourceTree = ""; }; BF63EF6821D0FDB5001D38C5 /* XMLHeaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLHeaderTests.swift; sourceTree = ""; }; BF63EF6A21D10284001D38C5 /* XMLElementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLElementTests.swift; sourceTree = ""; }; + BF8171F121D3D03E00901EB0 /* SharedBoxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedBoxTests.swift; sourceTree = ""; }; BF94579E21CBB497005ACFDE /* NullBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NullBox.swift; sourceTree = ""; }; BF94579F21CBB497005ACFDE /* KeyedBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyedBox.swift; sourceTree = ""; }; BF9457A021CBB497005ACFDE /* UnkeyedBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnkeyedBox.swift; sourceTree = ""; }; @@ -247,6 +251,7 @@ BF63EF0521CD7A74001D38C5 /* URLBox.swift */, BF9457A021CBB497005ACFDE /* UnkeyedBox.swift */, BF94579F21CBB497005ACFDE /* KeyedBox.swift */, + BF63EF1721CEB6BD001D38C5 /* SharedBox.swift */, ); path = Box; sourceTree = ""; @@ -280,6 +285,7 @@ BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */, BF9457C721CBB516005ACFDE /* UnkeyedBoxTests.swift */, BF9457BF21CBB516005ACFDE /* KeyedBoxTests.swift */, + BF8171F121D3D03E00901EB0 /* SharedBoxTests.swift */, ); path = Box; sourceTree = ""; @@ -493,6 +499,7 @@ BF9457B721CBB4DB005ACFDE /* XMLHeader.swift in Sources */, BF9457BB21CBB4DB005ACFDE /* XMLKey.swift in Sources */, OBJ_48 /* DecodingErrorExtension.swift in Sources */, + BF63EF1821CEB6BD001D38C5 /* SharedBox.swift in Sources */, BF9457DB21CBB5D2005ACFDE /* DateBox.swift in Sources */, BF63EF0621CD7A74001D38C5 /* URLBox.swift in Sources */, OBJ_49 /* XMLDecoder.swift in Sources */, @@ -565,6 +572,7 @@ BF63EF0C21CD7F28001D38C5 /* EmptyTests.swift in Sources */, BF9457F721CBB6BC005ACFDE /* DataTests.swift in Sources */, BF9457EE21CBB6BC005ACFDE /* IntTests.swift in Sources */, + BF8171F221D3D03E00901EB0 /* SharedBoxTests.swift in Sources */, OBJ_87 /* PlantCatalog.swift in Sources */, BF9457C921CBB516005ACFDE /* KeyedBoxTests.swift in Sources */, OBJ_88 /* PlantTest.swift in Sources */, @@ -575,8 +583,8 @@ BF9457F621CBB6BC005ACFDE /* KeyedTests.swift in Sources */, BF9457C821CBB516005ACFDE /* BoolBoxTests.swift in Sources */, D1E0C85321D8E65E0042A261 /* ErrorContextTest.swift in Sources */, + D1CB1EF521EA9599009CAF02 /* RJITest.swift in Sources */, BF9457F421CBB6BC005ACFDE /* UIntTests.swift in Sources */, - OBJ_89 /* RJITest.swift in Sources */, BF9457F121CBB6BC005ACFDE /* FloatTests.swift in Sources */, BF9457EF21CBB6BC005ACFDE /* NullTests.swift in Sources */, OBJ_90 /* RelationshipsTest.swift in Sources */,