diff --git a/Sources/SwiftRoaring/RoaringBitmap.swift b/Sources/SwiftRoaring/RoaringBitmap.swift index 2b6d8f3..0a5d1a5 100644 --- a/Sources/SwiftRoaring/RoaringBitmap.swift +++ b/Sources/SwiftRoaring/RoaringBitmap.swift @@ -1,3 +1,4 @@ +import Foundation import croaring /// @@ -8,9 +9,12 @@ public typealias RoaringStatistics = roaring_statistics_t /// /// Swift wrapper for CRoaring (a C/C++ implementation at https://github.com/RoaringBitmap/CRoaring) /// -public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, - Hashable, ExpressibleByArrayLiteral { +public final class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, + Hashable, ExpressibleByArrayLiteral, SetAlgebra, Codable { + + @usableFromInline var ptr: UnsafeMutablePointer + public typealias Element = UInt32 ///////////////////////////////////////////////////////////////////////////// @@ -19,14 +23,14 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Creates a new bitmap (initially empty) /// - public init() { + required public init() { self.ptr = croaring.roaring_bitmap_create()! } /// /// Creates a new bitmap using a given ptr /// - private init(ptr: UnsafeMutablePointer) { + required public init(ptr: UnsafeMutablePointer) { self.ptr = ptr } @@ -72,34 +76,39 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// caller is /// responsible for memory management. /// - public func intersection(_ x: RoaringBitmap) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_and(self.ptr, x.ptr)) + @inlinable @inline(__always) + public func intersection(_ x: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_and(self.ptr, x.ptr)) } /// /// Computes the intersection between two bitmaps and returns new bitmap. The /// caller is /// responsible for memory management. /// - public static func &(left: RoaringBitmap, right: RoaringBitmap) -> RoaringBitmap { - return left.intersection(right) + @inlinable @inline(__always) + public static func &(lhs: RoaringBitmap, rhs: RoaringBitmap) -> Self { + Self(ptr: croaring.roaring_bitmap_and(lhs.ptr, rhs.ptr)) } /// /// Inplace version modifies x1, x1 == x2 is allowed /// + @inlinable @inline(__always) public func formIntersection(_ x: RoaringBitmap) { croaring.roaring_bitmap_and_inplace(self.ptr, x.ptr) } /// /// Inplace version modifies x1, x1 == x2 is allowed /// - public static func &=(left: RoaringBitmap, right: RoaringBitmap) { - left.formIntersection(right) + @inlinable @inline(__always) + public static func &=(lhs: RoaringBitmap, rhs: RoaringBitmap) { + lhs.formIntersection(rhs) } /// /// Computes the size of the intersection between two bitmaps. /// + @inlinable @inline(__always) public func intersectionCount(_ x: RoaringBitmap) -> UInt64 { return croaring.roaring_bitmap_and_cardinality(self.ptr, x.ptr) } @@ -107,6 +116,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Check whether two bitmaps intersect. /// + @inlinable @inline(__always) public func intersect(_ x: RoaringBitmap) -> Bool { return croaring.roaring_bitmap_intersect(self.ptr, x.ptr) } @@ -118,6 +128,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// The Jaccard index is undefined if both bitmaps are empty. /// + @inlinable @inline(__always) public func jaccardIndex(_ x: RoaringBitmap) -> Double { return croaring.roaring_bitmap_jaccard_index(self.ptr, x.ptr) } @@ -132,6 +143,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Computes the size of the difference (andnot) between two bitmaps. /// + @inlinable @inline(__always) public func subtractingCount(_ x: RoaringBitmap) -> UInt64 { return croaring.roaring_bitmap_andnot_cardinality(self.ptr, x.ptr) } @@ -139,6 +151,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Returns the number of elements in range [min, max). /// + @inlinable @inline(__always) public func rangeCardinality(min: UInt64, max: UInt64) -> UInt64 { return croaring.roaring_bitmap_range_cardinality(self.ptr, min, max) } @@ -146,6 +159,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Computes the size of the symmetric difference (andnot) between two bitmaps. /// + @inlinable @inline(__always) public func symmetricDifferenceCount(_ x: RoaringBitmap) -> UInt64 { return croaring.roaring_bitmap_xor_cardinality(self.ptr, x.ptr) } @@ -154,41 +168,45 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Computes the union between two bitmaps and returns new bitmap. The caller is /// responsible for memory management. /// - public func union(_ x: RoaringBitmap) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_or(self.ptr, x.ptr)) + @inlinable @inline(__always) + public func union(_ x: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_or(self.ptr, x.ptr)) } /// /// Computes the union between two bitmaps and returns new bitmap. The caller is /// responsible for memory management. /// - public static func |(left: RoaringBitmap, right: RoaringBitmap) -> RoaringBitmap { - return left.union(right) + @inlinable @inline(__always) + public static func |(lhs: RoaringBitmap, rhs: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_or(lhs.ptr, rhs.ptr)) } /// /// Inplace version of roaring_bitmap_or, modifies x1. /// + @inlinable @inline(__always) public func formUnion(_ x: RoaringBitmap) { croaring.roaring_bitmap_or_inplace(self.ptr, x.ptr) } /// /// Inplace version of `roaring_bitmap_or, modifies` x1. /// - public static func |=(left: RoaringBitmap, right: RoaringBitmap) { - left.formUnion(right) + @inlinable @inline(__always) + public static func |=(lhs: RoaringBitmap, rhs: RoaringBitmap) { + lhs.formUnion(rhs) } /// /// Compute the union of 'number' bitmaps. See also `roaring_bitmap_or_many_heap`. /// Caller is responsible for freeing the result. /// - public func unionMany(_ xs: [RoaringBitmap]) -> RoaringBitmap { + public func unionMany(_ xs: [RoaringBitmap]) -> Self { let ptr = UnsafeMutablePointer>>.allocate(capacity: xs.count + 1) ptr[0] = UnsafePointer(self.ptr) for (index, bitmap) in xs.enumerated() { ptr[index + 1] = UnsafePointer(bitmap.ptr) } - return RoaringBitmap(ptr: croaring.roaring_bitmap_or_many(xs.count + 1, ptr)) + return Self(ptr: croaring.roaring_bitmap_or_many(xs.count + 1, ptr)) } /// /// Compute the union of 'number' bitmaps using a heap. This can @@ -196,41 +214,45 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// a naive algorithm. Caller is responsible for freeing the /// result. /// - public func unionManyHeap(_ xs: [RoaringBitmap]) -> RoaringBitmap { + public func unionManyHeap(_ xs: [RoaringBitmap]) -> Self { let ptr = UnsafeMutablePointer>>.allocate(capacity: xs.count + 1) ptr[0] = UnsafePointer(self.ptr) for (index, bitmap) in xs.enumerated() { ptr[index + 1] = UnsafePointer(bitmap.ptr) } - return RoaringBitmap(ptr: croaring.roaring_bitmap_or_many_heap(UInt32(xs.count + 1), ptr)) + return Self(ptr: croaring.roaring_bitmap_or_many_heap(UInt32(xs.count + 1), ptr)) } /// /// Computes the symmetric difference (xor) between two bitmaps /// and returns new bitmap. The caller is responsible for memory management. /// - public func symmetricDifference(_ x: RoaringBitmap) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_xor(self.ptr, x.ptr)) + @inlinable @inline(__always) + public func symmetricDifference(_ x: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_xor(self.ptr, x.ptr)) } /// /// Computes the symmetric difference (xor) between two bitmaps /// and returns new bitmap. The caller is responsible for memory management. /// - public static func ^(left: RoaringBitmap, right: RoaringBitmap) -> RoaringBitmap { - return left.symmetricDifference(right) + @inlinable @inline(__always) + public static func ^(lhs: RoaringBitmap, rhs: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_xor(lhs.ptr, rhs.ptr)) } /// /// Inplace version of roaring_bitmap_xor, modifies x1. x1 != x2. /// + @inlinable @inline(__always) public func formSymmetricDifference(_ x: RoaringBitmap) { croaring.roaring_bitmap_xor_inplace(self.ptr, x.ptr) } /// /// Inplace version of roaring_bitmap_xor, modifies x1. x1 != x2. /// - public static func ^=(left: RoaringBitmap, right: RoaringBitmap) { - left.formSymmetricDifference(right) + @inlinable @inline(__always) + public static func ^=(lhs: RoaringBitmap, rhs: RoaringBitmap) { + lhs.formSymmetricDifference(rhs) } /// @@ -238,41 +260,45 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Caller is responsible for freeing the /// result. /// - public func symmetricDifferenceMany(_ xs: [RoaringBitmap]) -> RoaringBitmap { + public func symmetricDifferenceMany(_ xs: [RoaringBitmap]) -> Self { let ptr = UnsafeMutablePointer>>.allocate(capacity: xs.count + 1) ptr[0] = UnsafePointer(self.ptr) for (index, bitmap) in xs.enumerated() { ptr[index + 1] = UnsafePointer(bitmap.ptr) } - return RoaringBitmap(ptr: croaring.roaring_bitmap_xor_many(xs.count + 1, ptr)) + return Self(ptr: croaring.roaring_bitmap_xor_many(xs.count + 1, ptr)) } /// /// Computes the difference (andnot) between two bitmaps /// and returns new bitmap. The caller is responsible for memory management. /// - public func subtracting(_ x: RoaringBitmap) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_andnot(self.ptr, x.ptr)) + @inlinable @inline(__always) + public func subtracting(_ x: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_andnot(self.ptr, x.ptr)) } /// /// Computes the difference (andnot) between two bitmaps /// and returns new bitmap. The caller is responsible for memory management. /// - public static func -(left: RoaringBitmap, right: RoaringBitmap) -> RoaringBitmap { - return left.subtracting(right) + @inlinable @inline(__always) + public static func -(lhs: RoaringBitmap, rhs: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_andnot(lhs.ptr, rhs.ptr)) } /// /// Inplace version of roaring_bitmap_andnot, modifies x1. x1 != x2. /// + @inlinable @inline(__always) public func subtract(_ x: RoaringBitmap) { croaring.roaring_bitmap_andnot_inplace(self.ptr, x.ptr) } /// /// Inplace version of roaring_bitmap_andnot, modifies x1. x1 != x2. /// - public static func -=(left: RoaringBitmap, right: RoaringBitmap) { - left.subtract(right) + @inlinable @inline(__always) + public static func -=(lhs: RoaringBitmap, rhs: RoaringBitmap) { + lhs.subtract(rhs) } /// @@ -281,13 +307,15 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Return true if the two bitmaps contain the same elements. /// - public static func ==(left: RoaringBitmap, right: RoaringBitmap) -> Bool { - return croaring.roaring_bitmap_equals(left.ptr, right.ptr) + @inlinable @inline(__always) + public static func ==(lhs: RoaringBitmap, rhs: RoaringBitmap) -> Bool { + return croaring.roaring_bitmap_equals(lhs.ptr, rhs.ptr) } /// /// Return true if all the elements of ra1 are also in ra2. /// + @inlinable @inline(__always) public func isSubset(of x: RoaringBitmap) -> Bool { return croaring.roaring_bitmap_is_subset(self.ptr, x.ptr) } @@ -297,6 +325,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// greater /// than ra1. /// + @inlinable @inline(__always) public func isStrictSubset(of x: RoaringBitmap) -> Bool { return croaring.roaring_bitmap_is_strict_subset(self.ptr, x.ptr) } @@ -314,8 +343,9 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// The bitsetconversion conversion is a flag which determines /// whether container-container operations force a bitset conversion. /// - public func lazyUnion(_ x: RoaringBitmap, bitsetconversion: Bool) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_lazy_or(self.ptr, x.ptr, bitsetconversion)) + @inlinable @inline(__always) + public func lazyUnion(_ x: RoaringBitmap, bitsetconversion: Bool) -> Self { + return Self(ptr: croaring.roaring_bitmap_lazy_or(self.ptr, x.ptr, bitsetconversion)) } /// @@ -324,6 +354,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// The bitsetconversion conversion is a flag which determines /// whether container-container operations force a bitset conversion. /// + @inlinable @inline(__always) public func formLazyUnion(_ x: RoaringBitmap, bitsetconversion: Bool) { croaring.roaring_bitmap_lazy_or_inplace(self.ptr, x.ptr, bitsetconversion) } @@ -335,6 +366,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// roaring_bitmap_lazy_or /// or modified with roaring_bitmap_lazy_or_inplace. /// + @inlinable @inline(__always) public func repairAfterLazy() { croaring.roaring_bitmap_repair_after_lazy(self.ptr) } @@ -349,14 +381,16 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// to call roaring_bitmap_repair_after_lazy after executing "lazy" computations. /// It is safe to repeatedly call roaring_bitmap_lazy_xor_inplace on the result. /// - public func lazySymmetricDifference(_ x: RoaringBitmap) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_lazy_xor(self.ptr, x.ptr)) + @inlinable @inline(__always) + public func lazySymmetricDifference(_ x: RoaringBitmap) -> Self { + return Self(ptr: croaring.roaring_bitmap_lazy_xor(self.ptr, x.ptr)) } /// /// (For expert users who seek high performance.) /// Inplace version of roaring_bitmap_lazy_xor, modifies x1. x1 != x2 /// + @inlinable @inline(__always) public func formLazySymmetricDifference(_ x: RoaringBitmap) { croaring.roaring_bitmap_lazy_xor_inplace(self.ptr, x.ptr) } @@ -367,8 +401,9 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// range_end - range_start. /// Areas outside the range are passed through unchanged. /// - public func flip(rangeStart: UInt64, rangeEnd: UInt64) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_flip(self.ptr, rangeStart, rangeEnd)) + @inlinable @inline(__always) + public func flip(rangeStart: UInt64, rangeEnd: UInt64) -> Self { + return Self(ptr: croaring.roaring_bitmap_flip(self.ptr, rangeStart, rangeEnd)) } /// @@ -377,6 +412,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// range_end - range_start. /// Areas outside the range are passed through unchanged. /// + @inlinable @inline(__always) public func flipInplace(rangeStart: UInt64, rangeEnd: UInt64) { croaring.roaring_bitmap_flip_inplace(self.ptr, rangeStart, rangeEnd) } @@ -389,8 +425,9 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Copies a bitmap. This does memory allocation. The caller is responsible for /// memory management. /// - public func copy() -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_copy(self.ptr)) + @inlinable @inline(__always) + public func copy() -> Self { + return Self(ptr: croaring.roaring_bitmap_copy(self.ptr)) } /// @@ -409,6 +446,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Add value x /// + @inlinable @inline(__always) public func add(_ value: UInt32) { croaring.roaring_bitmap_add(self.ptr, value) } @@ -417,7 +455,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Add value n_args from pointer vals, faster than repeatedly calling /// roaring_bitmap_add /// - /// + @inlinable @inline(__always) public func addMany(values: [UInt32]) { croaring.roaring_bitmap_add_many(self.ptr, values.count, values) } @@ -426,6 +464,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Add value x /// Returns true if a new value was added, false if the value was already existing. /// + @inlinable @inline(__always) public func addCheck(_ value: UInt32) -> Bool { return croaring.roaring_bitmap_add_checked(self.ptr, value) } @@ -433,6 +472,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Add all values in range [min, max] /// + @inlinable @inline(__always) public func addRangeClosed(min: UInt32, max: UInt32) { croaring.roaring_bitmap_add_range_closed(self.ptr, min, max) } @@ -440,23 +480,54 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Add all values in range [min, max) /// + @inlinable @inline(__always) public func addRange(min: UInt64, max: UInt64) { croaring.roaring_bitmap_add_range(self.ptr, min, max) } + @inlinable @inline(__always) + public func insert(_ newMember: UInt32) -> (inserted: Bool, memberAfterInsert: UInt32) { + let contains = self.contains(newMember) + if !contains { + self.add(newMember) + } + return (!contains, newMember) + } + + @inlinable @inline(__always) + public func update(with newMember: UInt32) -> UInt32? { + let (inserted, _ ) = self.insert(newMember) + if inserted { + return nil + } else { + return newMember + } + } + /// /// Remove value x /// - public func remove(_ value: UInt32) { + @inlinable @inline(__always) + @discardableResult + public func remove(_ value: UInt32) -> UInt32? { + let result = self.contains(value) croaring.roaring_bitmap_remove(self.ptr, value) + if result { + return value + } + return nil } - /// Remove all values in range [min, max] */ + /// + /// Remove all values in range [min, max] + /// + @inlinable @inline(__always) public func removeRangeClosed(min: UInt32, max: UInt32) { croaring.roaring_bitmap_remove_range_closed(self.ptr, min, max) } - /// Remove all values in range [min, max) */ + /// Remove all values in range [min, max) + @inlinable @inline(__always) public func removeRange(min: UInt64, max: UInt64) { croaring.roaring_bitmap_remove_range(self.ptr, min, max) } @@ -471,6 +542,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Remove value x /// Returns true if a new value was removed, false if the value was not existing. /// + @inlinable @inline(__always) public func removeCheck(_ value: UInt32) -> Bool { return croaring.roaring_bitmap_remove_checked(self.ptr, value) } @@ -478,6 +550,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Frees the memory. /// + @inline(__always) private func free() { croaring.roaring_bitmap_free(self.ptr) } @@ -485,6 +558,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Empties the bitmap. /// + @inlinable @inline(__always) public func clear() { croaring.roaring_bitmap_clear(self.ptr) } @@ -492,6 +566,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Get the cardinality of the bitmap (number of elements). /// + @inlinable @inline(__always) public var count: UInt64 { return croaring.roaring_bitmap_get_cardinality(self.ptr) } @@ -499,6 +574,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Check if value x is present /// + @inlinable @inline(__always) public func contains(_ value: UInt32) -> Bool { return croaring.roaring_bitmap_contains(self.ptr, value) } @@ -506,12 +582,14 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Check whether a range of values from range_start (included) to range_end (excluded) is present /// + @inlinable @inline(__always) public func containsRange(start: UInt64, end: UInt64) -> Bool { return croaring.roaring_bitmap_contains_range(self.ptr, start, end) } /// /// Check whether the bitmap is empty /// + @inlinable @inline(__always) public var isEmpty: Bool { return croaring.roaring_bitmap_is_empty(self.ptr) } @@ -519,6 +597,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Print the content of the bitmap. /// + @inlinable @inline(__always) public func print() { croaring.roaring_bitmap_printf(self.ptr) } @@ -526,6 +605,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// /// Describe the inner structure of the bitmap. /// + @inlinable @inline(__always) public func describe() { croaring.roaring_bitmap_printf_describe(self.ptr) } @@ -564,17 +644,19 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Remove run-length encoding even when it is more space efficient /// return whether a change was applied /// - + @inlinable @inline(__always) public func removeRunCompression() -> Bool { return croaring.roaring_bitmap_remove_run_compression(self.ptr) } + /// /// convert array and bitmap containers to run containers when it is more /// efficient; /// also convert from run containers when more space efficient. Returns /// true if the result has at least one run container. /// Additional savings might be possible by calling shrinkToFit(). /// + @inlinable @inline(__always) public func runOptimize() -> Bool { return croaring.roaring_bitmap_run_optimize(self.ptr) } @@ -583,6 +665,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// If needed, reallocate memory to shrink the memory usage. Returns /// the number of bytes saved. /// + @inlinable @inline(__always) public func shrink() -> size_t { return croaring.roaring_bitmap_shrink_to_fit(self.ptr) } @@ -601,6 +684,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Returns how many bytes were written which should be /// roaring_bitmap_size_in_bytes(ra). /// + @inlinable @inline(__always) public func serialize(buffer: inout [Int8]) -> size_t { return croaring.roaring_bitmap_serialize(self.ptr, &buffer) } @@ -610,14 +694,44 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// see roaring_bitmap_portable_deserialize if you want a format that's /// compatible with Java and Go implementations /// - public static func deserialize(buffer: [Int8]) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_deserialize(buffer)!) + @inlinable @inline(__always) + public static func deserialize(buffer: [Int8]) -> Self { + return Self(ptr: croaring.roaring_bitmap_deserialize(buffer)!) + } + + /// + /// Encodable conformance + /// + public func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + let count = self.sizeInBytes() + var data = Data(count: count) + + data.withUnsafeMutableBytes { + let ptr = $0.baseAddress!.assumingMemoryBound(to: Int8.self) + croaring.roaring_bitmap_serialize(self.ptr, ptr) + } + try container.encode(data.base64EncodedString()) + } + + /// + /// Decodable conformance + /// + public init(from decoder: Decoder) throws { + var container = try decoder.unkeyedContainer() + + let buffer = try container.decode(String.self) + let data = Data(base64Encoded: buffer) + self.ptr = data!.withUnsafeBytes { + croaring.roaring_bitmap_deserialize($0)! + } } /// /// How many bytes are required to serialize this bitmap (NOT compatible /// with Java and Go versions) /// + @inlinable @inline(__always) public func sizeInBytes() -> size_t { return croaring.roaring_bitmap_size_in_bytes(self.ptr) } @@ -632,8 +746,9 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// overflow. For a safer approach, /// call roaring_bitmap_portable_deserialize_safe. /// - public static func portableDeserialize(buffer: [Int8]) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_portable_deserialize(buffer)!) + @inlinable @inline(__always) + public static func portableDeserialize(buffer: [Int8]) -> Self { + return Self(ptr: croaring.roaring_bitmap_portable_deserialize(buffer)!) } /// @@ -643,8 +758,9 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// https://github.com/RoaringBitmap/RoaringFormatSpec /// In case of failure, a null pointer is returned. /// - public static func portableDeserializeSafe(buffer: [Int8], maxbytes: size_t) -> RoaringBitmap { - return RoaringBitmap(ptr: croaring.roaring_bitmap_portable_deserialize_safe(buffer, maxbytes)!) + @inlinable @inline(__always) + public static func portableDeserializeSafe(buffer: [Int8], maxbytes: size_t) -> Self { + return Self(ptr: croaring.roaring_bitmap_portable_deserialize_safe(buffer, maxbytes)!) } /// @@ -654,6 +770,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// the Java and Go versions. See format specification at /// https://github.com/RoaringBitmap/RoaringFormatSpec /// + @inlinable @inline(__always) public static func portableDeserializeSize(buffer: [Int8], maxbytes: size_t) -> size_t { return croaring.roaring_bitmap_portable_deserialize_size(buffer, maxbytes) } @@ -663,6 +780,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// with Java and Go versions). See format specification at /// https://github.com/RoaringBitmap/RoaringFormatSpec /// + @inlinable @inline(__always) public func portableSizeInBytes() -> size_t { return croaring.roaring_bitmap_portable_size_in_bytes(self.ptr) } @@ -676,6 +794,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// roaring_bitmap_portable_size_in_bytes(ra). See format specification at /// https://github.com/RoaringBitmap/RoaringFormatSpec /// + @inlinable @inline(__always) public func portableSerialize(buffer: inout [Int8]) -> size_t { return croaring.roaring_bitmap_portable_serialize(self.ptr, &buffer) } @@ -685,6 +804,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// function returns true and set the value to the the given rank. /// Otherwise, it returns false. /// + @inlinable @inline(__always) public func select(rank: UInt32, value: UInt32) -> Bool { var cpy = value return croaring.roaring_bitmap_select(self.ptr, rank, &cpy) @@ -694,6 +814,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// roaring_bitmap_rank returns the number of integers that are smaller or equal /// to x. /// + @inlinable @inline(__always) public func rank(value: UInt32) -> UInt64 { return croaring.roaring_bitmap_rank(self.ptr, value) } @@ -702,6 +823,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// roaring_bitmap_smallest returns the smallest value in the set. /// Returns UINT32_MAX if the set is empty. /// + @inlinable @inline(__always) public func min() -> UInt32 { return croaring.roaring_bitmap_minimum(self.ptr) } @@ -710,6 +832,7 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// roaring_bitmap_smallest returns the greatest value in the set. /// Returns 0 if the set is empty. /// + @inlinable @inline(__always) public func max() -> UInt32 { return croaring.roaring_bitmap_maximum(self.ptr) } @@ -719,7 +842,6 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// Collect statistics about the bitmap, see RoaringStatistics.swift for /// a description of RoaringStatistics /// - public func statistics() -> RoaringStatistics { var stats = RoaringStatistics() croaring.roaring_bitmap_statistics(self.ptr, &stats) @@ -744,12 +866,10 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, } mutating public func next() -> UInt32? { - if i.pointee.has_value { - let val = i.pointee.current_value - croaring.roaring_advance_uint32_iterator(self.i) - return val - } - return nil + guard i.pointee.has_value else { return nil } + let val = i.pointee.current_value + croaring.roaring_advance_uint32_iterator(self.i) + return val } } @@ -768,13 +888,17 @@ public class RoaringBitmap: Sequence, Equatable, CustomStringConvertible, /// hash value for the bitset, this is expensive and should be buffered /// for performance /// - public func hash(into hasher: inout Hasher) { + public var hashValue: Int { let b: UInt32 = 31 var hash: UInt32 = 0 for i in self { hash = hash &* b &+ i } - let hashValue = Int(truncatingIfNeeded: hash) - hashValue.hash(into: &hasher) + return Int(hash) + } + + public func hash(into hasher: inout Hasher) { + let hash = self.hashValue + hash.hash(into: &hasher) } } diff --git a/Tests/swiftRoaringTests/swiftRoaringTests.swift b/Tests/swiftRoaringTests/swiftRoaringTests.swift index 8ac70ac..7fa18a8 100644 --- a/Tests/swiftRoaringTests/swiftRoaringTests.swift +++ b/Tests/swiftRoaringTests/swiftRoaringTests.swift @@ -331,7 +331,23 @@ class swiftRoaringTests: XCTestCase { } func testHashValue() { - print(rbm.hashValue) + let r = RoaringBitmap(min: 0, max: 100, step: 2) + XCTAssert(r.hashValue == 3801178162) + } + + func testCodable() { + let enc = JSONEncoder() + let dec = JSONDecoder() + + var r = RoaringBitmap(min: 0, max: 100, step: 2) + + let a = try! enc.encode(r) + + let s = String(data: a, encoding: .utf8)! + print(s.count) + let b = try! dec.decode(RoaringBitmap.self, from: a) + + XCTAssert(r == b) } func makeSets() -> (RoaringBitmap, RoaringBitmap, Set, Set) {