diff --git a/.travis.yml b/.travis.yml index 544690e..87d1613 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode9.3 +osx_image: xcode10.0 script: - xcrun xcodebuild -project BTree.xcodeproj -scheme BTree-macOS test - xcrun xcodebuild -project BTree.xcodeproj -scheme BTree-iOS diff --git a/Sources/BTree.swift b/Sources/BTree.swift index 18b0b05..4fefd43 100644 --- a/Sources/BTree.swift +++ b/Sources/BTree.swift @@ -1071,3 +1071,7 @@ extension BTree { return suffix(from: start).prefix(through: stop) } } + +#if swift(>=4.2) +extension BTree:Codable where Key: Codable, Value:Codable {} +#endif diff --git a/Sources/BTreeIndex.swift b/Sources/BTreeIndex.swift index ace0ce8..17e8b2b 100644 --- a/Sources/BTreeIndex.swift +++ b/Sources/BTreeIndex.swift @@ -192,3 +192,9 @@ internal struct BTreeWeakPath: BTreePath { } } } + +#if swift(>=4.2) +extension BTreeWeakPath: Codable where Key: Codable, Value: Codable {} + +extension BTreeIndex: Codable where Key: Codable, Value: Codable {} +#endif diff --git a/Sources/BTreeIterator.swift b/Sources/BTreeIterator.swift index 28c74de..409b737 100644 --- a/Sources/BTreeIterator.swift +++ b/Sources/BTreeIterator.swift @@ -54,7 +54,7 @@ public struct BTreeValueIterator: IteratorProtocol { /// An iterator for the keys stored in a B-tree without a value. public struct BTreeKeyIterator: IteratorProtocol { - internal typealias Base = BTreeIterator + internal typealias Base = BTreeIterator fileprivate var base: Base internal init(_ base: Base) { @@ -147,3 +147,15 @@ internal struct BTreeStrongPath: BTreePath { } } } + +#if swift(>=4.2) +extension EmptyKey: Codable {} + +extension BTreeKeyIterator: Codable where Key: Codable {} + +extension BTreeValueIterator: Codable where Value: Codable {} + +extension BTreeIterator: Codable where Key: Codable, Value: Codable {} + +extension BTreeStrongPath: Codable where Key: Codable, Value: Codable {} +#endif diff --git a/Sources/BTreeNode.swift b/Sources/BTreeNode.swift index e01e395..f4cf726 100644 --- a/Sources/BTreeNode.swift +++ b/Sources/BTreeNode.swift @@ -611,3 +611,48 @@ extension BTreeNode { } } +#if swift(>=4.2) +extension BTreeNode: Codable where Key: Codable, Value: Codable { + // Swift's tuples do not support Codable yet, so we have to generate this manually + + enum CodingKeys: String, CodingKey { + case elements + case children + case count + case _order + case _depth + } + + struct Pair: Codable { + var key: Key + var value: Value + init(_ key: Key, _ value: Value) { + self.key = key + self.value = value + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.elements.map({Pair($0.0, $0.1)}), forKey: .elements) + try container.encode(children, forKey: .children) + try container.encode(count, forKey: .count) + try container.encode(_order, forKey: ._order) + try container.encode(_depth, forKey: ._depth) + } + + convenience init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let elements = try container.decode(Array>.self, forKey: .elements) + let children = try container.decode(Array>.self, forKey: .children) + let count = try container.decode(Int.self, forKey: .count) + let _order = try container.decode(Int32.self, forKey: ._order) + let _depth = try container.decode(Int32.self, forKey: ._depth) + assert(_depth == (children.count == 0 ? 0 : children[0]._depth + 1)) + self.init(order: numericCast(_order), + elements: elements.map({ ($0.key, $0.value) }), + children: children, + count: count) + } +} +#endif diff --git a/Sources/List.swift b/Sources/List.swift index 7151fc5..0d0cb59 100644 --- a/Sources/List.swift +++ b/Sources/List.swift @@ -565,3 +565,7 @@ extension List { return result } } + +#if swift(>=4.2) +extension List: Codable where Element: Codable {} +#endif diff --git a/Sources/Map.swift b/Sources/Map.swift index 5169760..95280d1 100644 --- a/Sources/Map.swift +++ b/Sources/Map.swift @@ -564,3 +564,7 @@ extension Map { return excluding(SortedSet(keys)) } } + +#if swift(>=4.2) +extension Map: Codable where Key: Codable, Value: Codable {} +#endif diff --git a/Sources/SortedBag.swift b/Sources/SortedBag.swift index 5d72719..9e22948 100644 --- a/Sources/SortedBag.swift +++ b/Sources/SortedBag.swift @@ -24,7 +24,7 @@ /// /// - SeeAlso: `SortedSet` public struct SortedBag: SetAlgebra { - internal typealias Tree = BTree + internal typealias Tree = BTree /// The b-tree that serves as storage. internal fileprivate(set) var tree: Tree @@ -54,7 +54,7 @@ extension SortedBag { /// /// - Complexity: O(*n* * log(*n*)), where *n* is the number of items in the sequence. public init(_ elements: S) where S.Element == Element { - self.init(Tree(sortedElements: elements.sorted().lazy.map { ($0, ()) }, dropDuplicates: false)) + self.init(Tree(sortedElements: elements.sorted().lazy.map { ($0, EmptyValue.def) }, dropDuplicates: false)) } /// Create a bag from a sorted finite sequence of items. @@ -62,7 +62,7 @@ extension SortedBag { /// /// - Complexity: O(*n*), where *n* is the number of items in the sequence. public init(sortedElements elements: S) where S.Element == Element { - self.init(Tree(sortedElements: elements.lazy.map { ($0, ()) }, dropDuplicates: false)) + self.init(Tree(sortedElements: elements.lazy.map { ($0, EmptyValue.def) }, dropDuplicates: false)) } /// Create a bag with the specified list of items. @@ -75,7 +75,7 @@ extension SortedBag { extension SortedBag: BidirectionalCollection { //MARK: CollectionType - public typealias Index = BTreeIndex + public typealias Index = BTreeIndex public typealias Iterator = BTreeKeyIterator public typealias SubSequence = SortedBag @@ -442,7 +442,7 @@ extension SortedBag { /// Returns the index of the first instance of a given member, or `nil` if the member is not present in the bag. /// /// - Complexity: O(log(`count`)) - public func index(of member: Element) -> BTreeIndex? { + public func index(of member: Element) -> BTreeIndex? { return tree.index(forKey: member, choosing: .first) } @@ -451,7 +451,7 @@ extension SortedBag { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the bag.) /// /// - Complexity: O(log(`count`)) - public func indexOfFirstElement(after element: Element) -> BTreeIndex? { + public func indexOfFirstElement(after element: Element) -> BTreeIndex? { let index = tree.index(forInserting: element, at: .last) if tree.offset(of: index) == tree.count { return nil } return index @@ -462,7 +462,7 @@ extension SortedBag { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the bag.) /// /// - Complexity: O(log(`count`)) - public func indexOfFirstElement(notBefore element: Element) -> BTreeIndex? { + public func indexOfFirstElement(notBefore element: Element) -> BTreeIndex? { let index = tree.index(forInserting: element, at: .first) if tree.offset(of: index) == tree.count { return nil } return index @@ -473,7 +473,7 @@ extension SortedBag { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the bag.) /// /// - Complexity: O(log(`count`)) - public func indexOfLastElement(before element: Element) -> BTreeIndex? { + public func indexOfLastElement(before element: Element) -> BTreeIndex? { var index = tree.index(forInserting: element, at: .first) if tree.offset(of: index) == 0 { return nil } tree.formIndex(before: &index) @@ -485,7 +485,7 @@ extension SortedBag { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the bag.) /// /// - Complexity: O(log(`count`)) - public func indexOfLastElement(notAfter element: Element) -> BTreeIndex? { + public func indexOfLastElement(notAfter element: Element) -> BTreeIndex? { var index = tree.index(forInserting: element, at: .last) if tree.offset(of: index) == 0 { return nil } tree.formIndex(before: &index) @@ -600,7 +600,7 @@ extension SortedBag { /// - Complexity: O(log(`count`)) @discardableResult public mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) { - tree.insert((newMember, ()), at: .after) + tree.insert((newMember, EmptyValue.def), at: .after) return (true, newMember) } @@ -617,7 +617,7 @@ extension SortedBag { /// - Returns: Always returns `nil`, to satisfy the syntactic requirements of the `SetAlgebra` protocol. @discardableResult public mutating func update(with newMember: Element) -> Element? { - tree.insert((newMember, ()), at: .first) + tree.insert((newMember, EmptyValue.def), at: .first) return nil } } @@ -984,3 +984,7 @@ extension SortedBag where Element: Strideable { } } + +#if swift(>=4.2) +extension SortedBag: Codable where Element: Codable {} +#endif diff --git a/Sources/SortedSet.swift b/Sources/SortedSet.swift index 2145994..9c93d15 100644 --- a/Sources/SortedSet.swift +++ b/Sources/SortedSet.swift @@ -20,7 +20,7 @@ /// /// - SeeAlso: `SortedBag` public struct SortedSet: SetAlgebra { - internal typealias Tree = BTree + internal typealias Tree = BTree /// The b-tree that serves as storage. internal fileprivate(set) var tree: Tree @@ -43,7 +43,7 @@ extension SortedSet { /// /// - Complexity: O(*n* * log(*n*)), where *n* is the number of items in the sequence. public init(_ elements: S) where S.Element == Element { - self.init(Tree(sortedElements: elements.sorted().lazy.map { ($0, ()) }, dropDuplicates: true)) + self.init(Tree(sortedElements: elements.sorted().lazy.map { ($0, EmptyValue.def) }, dropDuplicates: true)) } /// Create a set from a sorted finite sequence of items. @@ -51,7 +51,7 @@ extension SortedSet { /// /// - Complexity: O(*n*), where *n* is the number of items in the sequence. public init(sortedElements elements: S) where S.Element == Element { - self.init(Tree(sortedElements: elements.lazy.map { ($0, ()) }, dropDuplicates: true)) + self.init(Tree(sortedElements: elements.lazy.map { ($0, EmptyValue.def) }, dropDuplicates: true)) } /// Create a set with the specified list of items. @@ -64,7 +64,7 @@ extension SortedSet { extension SortedSet: BidirectionalCollection { //MARK: CollectionType - public typealias Index = BTreeIndex + public typealias Index = BTreeIndex public typealias Iterator = BTreeKeyIterator public typealias SubSequence = SortedSet @@ -420,7 +420,7 @@ extension SortedSet { /// Returns the index of a given member, or `nil` if the member is not present in the set. /// /// - Complexity: O(log(`count`)) - public func index(of member: Element) -> BTreeIndex? { + public func index(of member: Element) -> BTreeIndex? { return tree.index(forKey: member) } @@ -429,7 +429,7 @@ extension SortedSet { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the set.) /// /// - Complexity: O(log(`count`)) - public func indexOfFirstElement(after element: Element) -> BTreeIndex? { + public func indexOfFirstElement(after element: Element) -> BTreeIndex? { let index = tree.index(forInserting: element, at: .last) if tree.offset(of: index) == tree.count { return nil } return index @@ -440,7 +440,7 @@ extension SortedSet { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the set.) /// /// - Complexity: O(log(`count`)) - public func indexOfFirstElement(notBefore element: Element) -> BTreeIndex? { + public func indexOfFirstElement(notBefore element: Element) -> BTreeIndex? { let index = tree.index(forInserting: element, at: .first) if tree.offset(of: index) == tree.count { return nil } return index @@ -451,7 +451,7 @@ extension SortedSet { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the set.) /// /// - Complexity: O(log(`count`)) - public func indexOfLastElement(before element: Element) -> BTreeIndex? { + public func indexOfLastElement(before element: Element) -> BTreeIndex? { var index = tree.index(forInserting: element, at: .first) if tree.offset(of: index) == 0 { return nil } tree.formIndex(before: &index) @@ -463,7 +463,7 @@ extension SortedSet { /// This function never returns `endIndex`. (If it returns non-nil, the returned index can be used to subscript the set.) /// /// - Complexity: O(log(`count`)) - public func indexOfLastElement(notAfter element: Element) -> BTreeIndex? { + public func indexOfLastElement(notAfter element: Element) -> BTreeIndex? { var index = tree.index(forInserting: element, at: .last) if tree.offset(of: index) == 0 { return nil } tree.formIndex(before: &index) @@ -572,7 +572,7 @@ extension SortedSet { /// - Complexity: O(log(`count`)) @discardableResult public mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) { - guard let old = tree.insertOrFind((newMember, ())) else { + guard let old = tree.insertOrFind((newMember, EmptyValue.def)) else { return (true, newMember) } return (false, old.0) @@ -589,7 +589,7 @@ extension SortedSet { /// comparison or some other means. @discardableResult public mutating func update(with newMember: Element) -> Element? { - return tree.insertOrReplace((newMember, ()))?.0 + return tree.insertOrReplace((newMember, EmptyValue.def))?.0 } } @@ -910,3 +910,17 @@ extension SortedSet where Element: Strideable { } } } + +// Swift Void is not codable, and having 2 Codable extensions is not allowed +// for BTree and for BTree +// +// thus the need for EmptyValue +public struct EmptyValue { + static let def = EmptyValue() +} + +#if swift(>=4.2) +extension EmptyValue: Codable {} + +extension SortedSet: Codable where Element: Codable {} +#endif diff --git a/Sources/Weak.swift b/Sources/Weak.swift index 8e7fd15..c4d7b31 100644 --- a/Sources/Weak.swift +++ b/Sources/Weak.swift @@ -13,3 +13,7 @@ internal struct Weak { self.value = value } } + +#if swift(>=4.2) +extension Weak: Codable where T: Codable {} +#endif diff --git a/Tests/BTreeTests/BTreeNodeTests.swift b/Tests/BTreeTests/BTreeNodeTests.swift index b421b17..62c3280 100644 --- a/Tests/BTreeTests/BTreeNodeTests.swift +++ b/Tests/BTreeTests/BTreeNodeTests.swift @@ -492,4 +492,22 @@ class BTreeNodeTests: XCTestCase { node.assertValid() assertEqualElements(node, (0..<100).map { (0, $0) }) } + + #if swift(>=4.2) + func testCanBeCodedDecoded() { + let node = maximalNode(depth: 1, order: 5) + let encoder = PropertyListEncoder() + guard let data = try? encoder.encode(node) else { + XCTFail("failed encode") + return + } + let decoder = PropertyListDecoder() + guard let decodedNode = try? decoder.decode(Node.self, from: data) else { + XCTFail("failed decode") + return + } + assertEqualElements(IteratorSequence(decodedNode.makeIterator()), + IteratorSequence(node.makeIterator())) + } + #endif } diff --git a/Tests/BTreeTests/BTreeTests.swift b/Tests/BTreeTests/BTreeTests.swift index a86bfe2..a154679 100644 --- a/Tests/BTreeTests/BTreeTests.swift +++ b/Tests/BTreeTests/BTreeTests.swift @@ -1236,4 +1236,22 @@ class BTreeTests: XCTestCase { tree.assertValid() assertEqualElements(tree, [(0, "0*"), (1, "1*"), (2, "2*"), (3, "3*"), (4, "4*")]) } + + #if swift(>=4.2) + func testCanBeCodedDecoded() { + let tree = Tree(minimalTree(depth: 2, order: 5)) + let encoder = PropertyListEncoder() + guard let data = try? encoder.encode(tree) else { + XCTFail("failed encode") + return + } + let decoder = PropertyListDecoder() + guard let decodedTree = try? decoder.decode(Tree.self, from: data) else { + XCTFail("failed decode") + return + } + assertEqualElements(IteratorSequence(decodedTree.makeIterator()), + IteratorSequence(tree.makeIterator())) + } + #endif } diff --git a/Tests/BTreeTests/ListTests.swift b/Tests/BTreeTests/ListTests.swift index 17a8787..75543ea 100644 --- a/Tests/BTreeTests/ListTests.swift +++ b/Tests/BTreeTests/ListTests.swift @@ -476,4 +476,22 @@ class ListTests: XCTestCase { assertEqualElements(l1 + l2, 0 ..< 20) } + + #if swift(>=4.2) + func testCanBeCodedDecoded() { + let list: List = [0, 1, 2, 3, 4] + let encoder = PropertyListEncoder() + guard let data = try? encoder.encode(list) else { + XCTFail("failed encode") + return + } + let decoder = PropertyListDecoder() + guard let decodedList = try? decoder.decode(List.self, from: data) else { + XCTFail("failed decode") + return + } + assertEqualElements(IteratorSequence(decodedList.makeIterator()), + IteratorSequence(list.makeIterator())) + } + #endif } diff --git a/Tests/BTreeTests/MapTests.swift b/Tests/BTreeTests/MapTests.swift index 0f0ce01..75d6b8d 100644 --- a/Tests/BTreeTests/MapTests.swift +++ b/Tests/BTreeTests/MapTests.swift @@ -440,4 +440,22 @@ class MapTests: XCTestCase { assertEqualElements(m.excluding([0, 5, 10, 15]), ([1 ..< 5, 6 ..< 10, 11 ..< 15, 16 ..< 20] as [CountableRange]).joined().map { (k) -> (Int, String) in (k, String(k)) }) assertEqualElements(m.excluding(-10 ..< 30), []) } + + #if swift(>=4.2) + func testCanBeCodedDecoded() { + let map = Map(sortedElements: (0..<100).map { ($0, String($0)) }) + let encoder = PropertyListEncoder() + guard let data = try? encoder.encode(map) else { + XCTFail("failed encode") + return + } + let decoder = PropertyListDecoder() + guard let decodedMap = try? decoder.decode(Map.self, from: data) else { + XCTFail("failed decode") + return + } + assertEqualElements(IteratorSequence(decodedMap.makeIterator()), + IteratorSequence(map.makeIterator())) + } + #endif } diff --git a/Tests/BTreeTests/SortedBagTests.swift b/Tests/BTreeTests/SortedBagTests.swift index 505121e..403efe5 100644 --- a/Tests/BTreeTests/SortedBagTests.swift +++ b/Tests/BTreeTests/SortedBagTests.swift @@ -982,4 +982,23 @@ class SortedBagTests: XCTestCase { assertEqualElements(copy, [0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18]) } + + #if swift(>=4.2) + func testCanBeCodedDecoded() { + let c = 100 + let bag = SortedBag((0 ..< c).map { 2 * $0 }.repeatEach(3)) + let encoder = PropertyListEncoder() + guard let data = try? encoder.encode(bag) else { + XCTFail("failed encode") + return + } + let decoder = PropertyListDecoder() + guard let decodedBag = try? decoder.decode(SortedBag.self, from: data) else { + XCTFail("failed decode") + return + } + assertEqualElements(IteratorSequence(decodedBag.makeIterator()), + IteratorSequence(bag.makeIterator())) + } + #endif } diff --git a/Tests/BTreeTests/SortedSetTests.swift b/Tests/BTreeTests/SortedSetTests.swift index b683572..b32a8e5 100644 --- a/Tests/BTreeTests/SortedSetTests.swift +++ b/Tests/BTreeTests/SortedSetTests.swift @@ -694,4 +694,23 @@ class SortedSetTests: XCTestCase { a.shift(startingAt: 15, by: -5) assertEqualElements(a, [0, 3, 5, 7, 8]) } + + #if swift(>=4.2) + func testCanBeCodedDecoded() { + let c = 1_000 + let set = SortedSet((0 ..< c).reversed()) + let encoder = PropertyListEncoder() + guard let data = try? encoder.encode(set) else { + XCTFail("failed encode") + return + } + let decoder = PropertyListDecoder() + guard let decodedSet = try? decoder.decode(SortedSet.self, from: data) else { + XCTFail("failed decode") + return + } + assertEqualElements(IteratorSequence(decodedSet.makeIterator()), + IteratorSequence(set.makeIterator())) + } + #endif }