Skip to content

Commit

Permalink
Replace UnkeyedBox with Array, refine KeyedStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxDesiatov committed May 12, 2019
1 parent 9078ab5 commit 237cc28
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 191 deletions.
49 changes: 2 additions & 47 deletions Sources/XMLCoder/Auxiliaries/Box/UnkeyedBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,9 @@
// Created by Vincent Esche on 11/20/18.
//

// Minimalist implementation of an order-preserving unkeyed box:
struct UnkeyedBox {
typealias Element = Box
typealias Unboxed = [Element]
typealias UnkeyedBox = [Box]

private(set) var unboxed: Unboxed

var count: Int {
return unboxed.count
}

subscript(index: Int) -> Element {
get {
return unboxed[index]
}
set {
unboxed[index] = newValue
}
}

init(_ unboxed: Unboxed = []) {
self.unboxed = unboxed
}

mutating func append(_ newElement: Element) {
unboxed.append(newElement)
}

mutating func insert(_ newElement: Element, at index: Int) {
unboxed.insert(newElement, at: index)
}
}

extension UnkeyedBox: Box {
extension Array: Box {
var isNull: Bool {
return false
}
Expand All @@ -47,17 +16,3 @@ extension UnkeyedBox: Box {
return nil
}
}

extension UnkeyedBox: Sequence {
typealias Iterator = Unboxed.Iterator

func makeIterator() -> Iterator {
return unboxed.makeIterator()
}
}

extension UnkeyedBox: CustomStringConvertible {
var description: String {
return "\(unboxed)"
}
}
106 changes: 25 additions & 81 deletions Sources/XMLCoder/Auxiliaries/KeyedStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,10 @@
//

struct KeyedStorage<Key: Hashable & Comparable, Value> {
struct Iterator: IteratorProtocol {
fileprivate var orderIterator: Order.Iterator
fileprivate var buffer: Buffer
mutating func next() -> (Key, Value)? {
guard
let key = orderIterator.next(),
let value = buffer[key]
else { return nil }

return (key, value)
}
}

typealias Buffer = [Key: Value]
typealias Order = [Key]
typealias Buffer = [(Key, Value)]
typealias KeyMap = [Key: [Int]]

fileprivate var order = Order()
fileprivate var keyMap = KeyMap()
fileprivate var buffer = Buffer()

var isEmpty: Bool {
Expand All @@ -34,23 +21,26 @@ struct KeyedStorage<Key: Hashable & Comparable, Value> {
}

var keys: [Key] {
return order
return buffer.map { $0.0 }
}

init<S>(_ sequence: S) where S: Sequence, S.Element == (Key, Value) {
order = sequence.map { $0.0 }
buffer = Dictionary(uniqueKeysWithValues: sequence)
buffer = Buffer()
keyMap = KeyMap()
sequence.forEach { key, value in append(value, at: key) }
}

subscript(key: Key) -> Value? {
get {
return buffer[key]
}
set {
if buffer[key] == nil {
order.append(key)
}
buffer[key] = newValue
subscript(key: Key) -> [Value] {
return keyMap[key]?.map { buffer[$0].1 } ?? []
}

mutating func append(_ value: Value, at key: Key) {
let i = buffer.count
buffer.append((key, value))
if keyMap[key] != nil {
keyMap[key]?.append(i)
} else {
keyMap[key] = [i]
}
}

Expand All @@ -68,67 +58,21 @@ struct KeyedStorage<Key: Hashable & Comparable, Value> {
}

extension KeyedStorage: Sequence {
func makeIterator() -> Iterator {
return Iterator(orderIterator: order.makeIterator(), buffer: buffer)
func makeIterator() -> Buffer.Iterator {
return buffer.makeIterator()
}
}

extension KeyedStorage: CustomStringConvertible {
var description: String {
let result = order.compactMap { (key: Key) -> String? in
guard let value = buffer[key] else { return nil }

return "\"\(key)\": \(value)"
let result = buffer.map { key, value in
"\"\(key)\": \(value)"
}.joined(separator: ", ")

return "[\(result)]"
}
}

private extension KeyedStorage where Key == String, Value == Box {
mutating func merge(value: String, at key: String) {
switch self[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(StringBox(value))
self[key] = unkeyedBox
case let stringBox as StringBox:
self[key] = UnkeyedBox([stringBox, StringBox(value)])
default:
self[key] = StringBox(value)
}
}

mutating func mergeElementsAttributes(from element: XMLCoderElement) {
let hasValue = element.value != nil

let key = element.key
let content = element.transformToBoxTree()
switch self[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(content)
self[key] = unkeyedBox
case let keyedBox as KeyedBox:
self[key] = UnkeyedBox([keyedBox, content])
case let box? where !hasValue:
self[key] = UnkeyedBox([box, content])
default:
self[key] = content
}
}

mutating func mergeNull(at key: String) {
switch self[key] {
case var unkeyedBox as UnkeyedBox:
unkeyedBox.append(NullBox())
self[key] = unkeyedBox
case let box?:
self[key] = UnkeyedBox([box, NullBox()])
default:
self[key] = NullBox()
}
}
}

extension KeyedStorage where Key == String, Value == Box {
func merge(element: XMLCoderElement) -> KeyedStorage<String, Box> {
var result = self
Expand All @@ -137,11 +81,11 @@ extension KeyedStorage where Key == String, Value == Box {
let hasAttributes = !element.attributes.isEmpty

if hasElements || hasAttributes {
result.mergeElementsAttributes(from: element)
result.append(element.transformToBoxTree(), at: element.key)
} else if let value = element.value {
result.merge(value: value, at: element.key)
result.append(StringBox(value), at: element.key)
} else {
result.mergeNull(at: element.key)
result.append(NullBox(), at: element.key)
}

return result
Expand Down
2 changes: 1 addition & 1 deletion Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct XMLCoderElement: Equatable {
// Handle attributed unkeyed value <foo attr="bar">zap</foo>
// Value should be zap. Detect only when no other elements exist
if elements.isEmpty, let value = value {
elements["value"] = StringBox(value)
elements.append(StringBox(value), at: "value")
}
let keyedBox = KeyedBox(elements: elements, attributes: attributes)

Expand Down
56 changes: 31 additions & 25 deletions Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,27 +89,27 @@ struct XMLKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol {
}

public func contains(_ key: Key) -> Bool {
let elementOrNil = container.withShared { keyedBox in
let elements = container.withShared { keyedBox in
keyedBox.elements[key.stringValue]
}

let attributeOrNil = container.withShared { keyedBox in
let attributes = container.withShared { keyedBox in
keyedBox.attributes[key.stringValue]
}

return (elementOrNil ?? attributeOrNil) != nil
return !elements.isEmpty || !attributes.isEmpty
}

public func decodeNil(forKey key: Key) throws -> Bool {
let elementOrNil = container.withShared { keyedBox in
let elements = container.withShared { keyedBox in
keyedBox.elements[key.stringValue]
}

let attributeOrNil = container.withShared { keyedBox in
let attributes = container.withShared { keyedBox in
keyedBox.attributes[key.stringValue]
}

let box = elementOrNil ?? attributeOrNil
let box = elements.first ?? attributes.first

return box?.isNull ?? true
}
Expand All @@ -118,11 +118,11 @@ struct XMLKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol {
_ type: T.Type, forKey key: Key
) throws -> T {
let attributeFound = container.withShared { keyedBox in
keyedBox.attributes[key.stringValue] != nil
!keyedBox.attributes[key.stringValue].isEmpty
}

let elementFound = container.withShared { keyedBox in
keyedBox.elements[key.stringValue] != nil || keyedBox.value != nil
!keyedBox.elements[key.stringValue].isEmpty || keyedBox.value != nil
}

if let type = type as? AnyEmptySequence.Type,
Expand All @@ -141,15 +141,15 @@ struct XMLKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol {
decoder.codingPath.append(key)
defer { decoder.codingPath.removeLast() }

let elementOrNil = self.container.withShared { keyedBox in
let elements = self.container.withShared { keyedBox in
keyedBox.elements[key.stringValue]
}

let attributeOrNil = self.container.withShared { keyedBox in
let attributes = self.container.withShared { keyedBox in
keyedBox.attributes[key.stringValue]
}

guard let value = elementOrNil ?? attributeOrNil else {
guard let value = elements.first ?? attributes.first else {
throw DecodingError.keyNotFound(key, DecodingError.Context(
codingPath: codingPath,
debugDescription:
Expand Down Expand Up @@ -188,15 +188,15 @@ struct XMLKeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol {
decoder.codingPath.append(key)
defer { decoder.codingPath.removeLast() }

let elementOrNil = container.withShared { keyedBox in
let elements = container.withShared { keyedBox in
keyedBox.elements[key.stringValue]
}

let attributeOrNil = container.withShared { keyedBox in
let attributes = container.withShared { keyedBox in
keyedBox.attributes[key.stringValue]
}

guard let value = elementOrNil ?? attributeOrNil else {
guard let value = elements.first ?? attributes.first else {
throw DecodingError.keyNotFound(key, DecodingError.Context(
codingPath: codingPath,
debugDescription:
Expand Down Expand Up @@ -286,16 +286,22 @@ extension XMLKeyedDecodingContainer {
)
}

let elementOrNil = container
.withShared { keyedBox -> KeyedBox.Element? in
let elements = container
.withShared { keyedBox -> [KeyedBox.Element] in
if ["value", ""].contains(key.stringValue) {
return keyedBox.elements[key.stringValue] ?? keyedBox.value
if let value = keyedBox.elements[key.stringValue].first {
return [value]
} else if let value = keyedBox.value {
return [value]
} else {
return []
}
} else {
return keyedBox.elements[key.stringValue]
}
}

let attributeOrNil = container.withShared { keyedBox in
let attributes = container.withShared { keyedBox in
keyedBox.attributes[key.stringValue]
}

Expand All @@ -313,15 +319,15 @@ extension XMLKeyedDecodingContainer {

// You can't decode sequences from attributes, but other strategies
// need special handling for empty sequences.
if strategy(key) != .attribute && elementOrNil == nil,
if strategy(key) != .attribute && elements.isEmpty,
let empty = (type as? AnyEmptySequence.Type)?.init() as? T {
return empty
}

switch strategy(key) {
case .attribute:
guard
let attributeBox = attributeOrNil
let attributeBox = attributes.first
else {
throw DecodingError.keyNotFound(key, DecodingError.Context(
codingPath: decoder.codingPath,
Expand All @@ -334,7 +340,7 @@ extension XMLKeyedDecodingContainer {
box = attributeBox
case .element:
guard
let elementBox = elementOrNil
let elementBox = elements.first
else {
throw DecodingError.keyNotFound(key, DecodingError.Context(
codingPath: decoder.codingPath,
Expand All @@ -347,7 +353,7 @@ extension XMLKeyedDecodingContainer {
box = elementBox
case .elementOrAttribute:
guard
let anyBox = elementOrNil ?? attributeOrNil
let anyBox = elements.first ?? attributes.first
else {
throw DecodingError.keyNotFound(key, DecodingError.Context(
codingPath: decoder.codingPath,
Expand Down Expand Up @@ -389,15 +395,15 @@ extension XMLKeyedDecodingContainer {
decoder.codingPath.append(key)
defer { decoder.codingPath.removeLast() }

let elementOrNil = container.withShared { keyedBox in
let elements = container.withShared { keyedBox in
keyedBox.elements[key.stringValue]
}

let attributeOrNil = container.withShared { keyedBox in
let attributes = container.withShared { keyedBox in
keyedBox.attributes[key.stringValue]
}

let box: Box = elementOrNil ?? attributeOrNil ?? NullBox()
let box: Box = elements.first ?? attributes.first ?? NullBox()
return XMLDecoderImplementation(
referencing: box,
options: decoder.options,
Expand Down

0 comments on commit 237cc28

Please sign in to comment.