Skip to content

Commit

Permalink
BTree supports Codable
Browse files Browse the repository at this point in the history
  • Loading branch information
bogdad committed Feb 16, 2020
1 parent 407fda7 commit 1913568
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 23 deletions.
4 changes: 4 additions & 0 deletions Sources/BTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions Sources/BTreeIndex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,9 @@ internal struct BTreeWeakPath<Key: Comparable, Value>: BTreePath {
}
}
}

#if swift(>=4.2)
extension BTreeWeakPath: Codable where Key: Codable, Value: Codable {}

extension BTreeIndex: Codable where Key: Codable, Value: Codable {}
#endif
14 changes: 13 additions & 1 deletion Sources/BTreeIterator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public struct BTreeValueIterator<Value>: IteratorProtocol {

/// An iterator for the keys stored in a B-tree without a value.
public struct BTreeKeyIterator<Key: Comparable>: IteratorProtocol {
internal typealias Base = BTreeIterator<Key, Void>
internal typealias Base = BTreeIterator<Key, EmptyValue>
fileprivate var base: Base

internal init(_ base: Base) {
Expand Down Expand Up @@ -147,3 +147,15 @@ internal struct BTreeStrongPath<Key: Comparable, Value>: 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
45 changes: 45 additions & 0 deletions Sources/BTreeNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Key: Codable, Value: Codable>: 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<Pair<Key, Value>>.self, forKey: .elements)
let children = try container.decode(Array<BTreeNode<Key, Value>>.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
4 changes: 4 additions & 0 deletions Sources/List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -565,3 +565,7 @@ extension List {
return result
}
}

#if swift(>=4.2)
extension List: Codable where Element: Codable {}
#endif
4 changes: 4 additions & 0 deletions Sources/Map.swift
Original file line number Diff line number Diff line change
Expand Up @@ -564,3 +564,7 @@ extension Map {
return excluding(SortedSet(keys))
}
}

#if swift(>=4.2)
extension Map: Codable where Key: Codable, Value: Codable {}
#endif
26 changes: 15 additions & 11 deletions Sources/SortedBag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
///
/// - SeeAlso: `SortedSet`
public struct SortedBag<Element: Comparable>: SetAlgebra {
internal typealias Tree = BTree<Element, Void>
internal typealias Tree = BTree<Element, EmptyValue>

/// The b-tree that serves as storage.
internal fileprivate(set) var tree: Tree
Expand Down Expand Up @@ -54,15 +54,15 @@ extension SortedBag {
///
/// - Complexity: O(*n* * log(*n*)), where *n* is the number of items in the sequence.
public init<S: Sequence>(_ 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.
/// If the sequence contains duplicate items, all of them are kept.
///
/// - Complexity: O(*n*), where *n* is the number of items in the sequence.
public init<S: Sequence>(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.
Expand All @@ -75,7 +75,7 @@ extension SortedBag {
extension SortedBag: BidirectionalCollection {
//MARK: CollectionType

public typealias Index = BTreeIndex<Element, Void>
public typealias Index = BTreeIndex<Element, EmptyValue>
public typealias Iterator = BTreeKeyIterator<Element>
public typealias SubSequence = SortedBag<Element>

Expand Down Expand Up @@ -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<Element, Void>? {
public func index(of member: Element) -> BTreeIndex<Element, EmptyValue>? {
return tree.index(forKey: member, choosing: .first)
}

Expand All @@ -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<Element, Void>? {
public func indexOfFirstElement(after element: Element) -> BTreeIndex<Element, EmptyValue>? {
let index = tree.index(forInserting: element, at: .last)
if tree.offset(of: index) == tree.count { return nil }
return index
Expand All @@ -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<Element, Void>? {
public func indexOfFirstElement(notBefore element: Element) -> BTreeIndex<Element, EmptyValue>? {
let index = tree.index(forInserting: element, at: .first)
if tree.offset(of: index) == tree.count { return nil }
return index
Expand All @@ -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<Element, Void>? {
public func indexOfLastElement(before element: Element) -> BTreeIndex<Element, EmptyValue>? {
var index = tree.index(forInserting: element, at: .first)
if tree.offset(of: index) == 0 { return nil }
tree.formIndex(before: &index)
Expand All @@ -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<Element, Void>? {
public func indexOfLastElement(notAfter element: Element) -> BTreeIndex<Element, EmptyValue>? {
var index = tree.index(forInserting: element, at: .last)
if tree.offset(of: index) == 0 { return nil }
tree.formIndex(before: &index)
Expand Down Expand Up @@ -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)
}

Expand All @@ -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
}
}
Expand Down Expand Up @@ -984,3 +984,7 @@ extension SortedBag where Element: Strideable {
}

}

#if swift(>=4.2)
extension SortedBag: Codable where Element: Codable {}
#endif
36 changes: 25 additions & 11 deletions Sources/SortedSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
///
/// - SeeAlso: `SortedBag`
public struct SortedSet<Element: Comparable>: SetAlgebra {
internal typealias Tree = BTree<Element, Void>
internal typealias Tree = BTree<Element, EmptyValue>

/// The b-tree that serves as storage.
internal fileprivate(set) var tree: Tree
Expand All @@ -43,15 +43,15 @@ extension SortedSet {
///
/// - Complexity: O(*n* * log(*n*)), where *n* is the number of items in the sequence.
public init<S: Sequence>(_ 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.
/// If the sequence contains duplicate items, only the last instance will be kept in the set.
///
/// - Complexity: O(*n*), where *n* is the number of items in the sequence.
public init<S: Sequence>(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.
Expand All @@ -64,7 +64,7 @@ extension SortedSet {
extension SortedSet: BidirectionalCollection {
//MARK: CollectionType

public typealias Index = BTreeIndex<Element, Void>
public typealias Index = BTreeIndex<Element, EmptyValue>
public typealias Iterator = BTreeKeyIterator<Element>
public typealias SubSequence = SortedSet<Element>

Expand Down Expand Up @@ -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<Element, Void>? {
public func index(of member: Element) -> BTreeIndex<Element, EmptyValue>? {
return tree.index(forKey: member)
}

Expand All @@ -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<Element, Void>? {
public func indexOfFirstElement(after element: Element) -> BTreeIndex<Element, EmptyValue>? {
let index = tree.index(forInserting: element, at: .last)
if tree.offset(of: index) == tree.count { return nil }
return index
Expand All @@ -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<Element, Void>? {
public func indexOfFirstElement(notBefore element: Element) -> BTreeIndex<Element, EmptyValue>? {
let index = tree.index(forInserting: element, at: .first)
if tree.offset(of: index) == tree.count { return nil }
return index
Expand All @@ -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<Element, Void>? {
public func indexOfLastElement(before element: Element) -> BTreeIndex<Element, EmptyValue>? {
var index = tree.index(forInserting: element, at: .first)
if tree.offset(of: index) == 0 { return nil }
tree.formIndex(before: &index)
Expand All @@ -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<Element, Void>? {
public func indexOfLastElement(notAfter element: Element) -> BTreeIndex<Element, EmptyValue>? {
var index = tree.index(forInserting: element, at: .last)
if tree.offset(of: index) == 0 { return nil }
tree.formIndex(before: &index)
Expand Down Expand Up @@ -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)
Expand All @@ -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
}
}

Expand Down Expand Up @@ -910,3 +910,17 @@ extension SortedSet where Element: Strideable {
}
}
}

// Swift Void is not codable, and having 2 Codable extensions is not allowed
// for BTree<Codable, Codable> and for BTree<Codable, Void>
//
// 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
4 changes: 4 additions & 0 deletions Sources/Weak.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ internal struct Weak<T: AnyObject> {
self.value = value
}
}

#if swift(>=4.2)
extension Weak: Codable where T: Codable {}
#endif
18 changes: 18 additions & 0 deletions Tests/BTreeTests/BTreeNodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
18 changes: 18 additions & 0 deletions Tests/BTreeTests/BTreeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading

0 comments on commit 1913568

Please sign in to comment.