Skip to content

BTree supports Codable #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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