Skip to content

Commit

Permalink
Merge branch 'master' of github.com:OpenKitten/BSON
Browse files Browse the repository at this point in the history
  • Loading branch information
Obbut committed Sep 30, 2016
2 parents 9aaacbc + 2ce727f commit d0e63ea
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 121 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ xcuserdata/
*.moved-aside
*.xccheckout
*.xcscmblueprint
docs
2 changes: 1 addition & 1 deletion Sources/Document+Operators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension Document : Equatable {
}
}

return true
return lhs.isArray == rhs.isArray
}

/// Returns true if `lhs` and `rhs` store the same serialized data.
Expand Down
63 changes: 57 additions & 6 deletions Sources/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,15 @@ public enum ElementType : UInt8 {

/// `Document` is a collection type that uses a BSON document as storage.
/// As such, it can be stored in a file or instantiated from BSON data.
///
///
/// Documents behave partially like an array, and partially like a dictionary.
/// For general information about BSON documents, see http://bsonspec.org/spec.html
public struct Document : Collection, ExpressibleByDictionaryLiteral, ExpressibleByArrayLiteral {
internal var storage: [UInt8]
internal var _count: Int? = nil
internal var invalid = false
internal var elementPositions = [Int]()
internal var isArray: Bool = false

// MARK: - Initialization from data

Expand All @@ -113,6 +114,7 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible

storage = Array(data[0..<length])
elementPositions = buildElementPositionsCache()
isArray = validatesAsArray()
}

/// Initializes this Doucment with an `Array` of `Byte`s - I.E: `[Byte]`
Expand All @@ -127,6 +129,7 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible

storage = Array(data[0..<length])
elementPositions = buildElementPositionsCache()
isArray = self.validatesAsArray()
}

/// Initializes an empty `Document`
Expand All @@ -141,10 +144,29 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible
///
/// - parameter elements: The `Dictionary`'s generics used to initialize this must be a `String` key and `Value` for the value
public init(dictionaryElements elements: [(String, Value)]) {
self.init()
for element in elements {
self.append(element.1, forKey: element.0)
storage = [5,0,0,0]

for (key, value) in elements {
// Append the key-value pair

// Add element to positions cache
elementPositions.append(storage.endIndex)

// Type identifier
storage.append(value.typeIdentifier)
// Key
storage.append(contentsOf: key.utf8)
// Key null terminator
storage.append(0x00)
// Value
storage.append(contentsOf: value.bytes)
}

storage.append(0x00)

updateDocumentHeader()

isArray = false
}

/// Initializes this `Document` as a `Dictionary` using a `Dictionary` literal
Expand All @@ -165,7 +187,29 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible
///
/// - parameter elements: The `Array` used to initialize the `Document` must be a `[Value]`
public init(array elements: [Value]) {
self.init(dictionaryElements: elements.enumerated().map { (index, value) in ("\(index)", value) })
storage = [5,0,0,0]

for (index, value) in elements.enumerated() {
// Append the values

// Add element to positions cache
elementPositions.append(storage.endIndex)

// Type identifier
storage.append(value.typeIdentifier)
// Key
storage.append(contentsOf: "\(index)".utf8)
// Key null terminator
storage.append(0x00)
// Value
storage.append(contentsOf: value.bytes)
}

storage.append(0x00)

updateDocumentHeader()

isArray = true
}

// MARK: - Manipulation & Extracting values
Expand Down Expand Up @@ -202,6 +246,8 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible

// Increase the bytecount
updateDocumentHeader()

isArray = false
}

/// Appends a `Value` to this `Document` where this `Document` acts like an `Array`
Expand All @@ -225,7 +271,8 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible

/// Updates this `Document`'s storage to contain the proper `Document` length header
internal mutating func updateDocumentHeader() {
storage.replaceSubrange(0..<4, with: Int32(storage.count).bytes)
var count = Int32(storage.count)
memcpy(&storage, &count, 4)
}

// MARK: - Collection
Expand Down Expand Up @@ -300,6 +347,10 @@ public struct Document : Collection, ExpressibleByDictionaryLiteral, Expressible
let val = getValue(atDataPosition: meta.dataPosition, withType: meta.type)
let length = getLengthOfElement(withDataPosition: meta.dataPosition, type: meta.type)

guard meta.dataPosition + length < storage.count else {
return nil
}

storage.removeSubrange(meta.elementTypePosition..<meta.dataPosition + length)

let removedLength = (meta.dataPosition + length) - meta.elementTypePosition
Expand Down
2 changes: 1 addition & 1 deletion Sources/ExtendedJSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ extension Document {
/// - returns: The JSON string. Depending on the type of document, the top level object will either be an array or object.
public func makeExtendedJSON() -> String {
var str: String
if self.validatesAsArray() {
if self.validatesAsArray() && isArray {
str = self.makeIterator().map { pair in
return pair.value.makeExtendedJSON()
}.reduce("[") { "\($0),\($1)" } + "]"
Expand Down
131 changes: 120 additions & 11 deletions Sources/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,126 @@ public extension String {
}

public protocol BSONBytesProtocol {}
extension Int : BSONBytesProtocol {}
extension Int64 : BSONBytesProtocol {}
extension Int32 : BSONBytesProtocol {}
extension Int16 : BSONBytesProtocol {}
extension Int8 : BSONBytesProtocol {}
extension UInt : BSONBytesProtocol {}
extension UInt64 : BSONBytesProtocol {}
extension UInt32 : BSONBytesProtocol {}
extension UInt16 : BSONBytesProtocol {}
extension UInt8 : BSONBytesProtocol {}
extension Double : BSONBytesProtocol {}

internal protocol BSONMakeBytesProtocol: BSONBytesProtocol {
func makeBytes() -> [UInt8]
}

extension Int : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
var integer = self
return withUnsafePointer(to: &integer) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Int>.size) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<Int>.size))
}
}
}
}

extension Int64 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [
UInt8(self & 0xFF),
UInt8((self >> 8) & 0xFF),
UInt8((self >> 16) & 0xFF),
UInt8((self >> 24) & 0xFF),
UInt8((self >> 32) & 0xFF),
UInt8((self >> 40) & 0xFF),
UInt8((self >> 48) & 0xFF),
UInt8((self >> 56) & 0xFF),
]
}
}

extension Int32 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [
UInt8(self & 0xFF),
UInt8((self >> 8) & 0xFF),
UInt8((self >> 16) & 0xFF),
UInt8((self >> 24) & 0xFF),
]
}
}

extension Int16 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [
UInt8((self >> 8) & 0xFF),
UInt8(self & 0xFF)
]
}
}

extension Int8 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [UInt8(self)]
}
}

extension UInt : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
var integer = self
return withUnsafePointer(to: &integer) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<UInt>.size) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<UInt>.size))
}
}
}
}

extension UInt64 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [
UInt8(self & 0xFF),
UInt8((self >> 8) & 0xFF),
UInt8((self >> 16) & 0xFF),
UInt8((self >> 24) & 0xFF),
UInt8((self >> 32) & 0xFF),
UInt8((self >> 40) & 0xFF),
UInt8((self >> 48) & 0xFF),
UInt8((self >> 56) & 0xFF),
]
}
}

extension UInt32 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [
UInt8(self & 0xFF),
UInt8((self >> 8) & 0xFF),
UInt8((self >> 16) & 0xFF),
UInt8((self >> 24) & 0xFF),
]
}
}

extension UInt16 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [
UInt8(self & 0xFF),
UInt8((self >> 8) & 0xFF)
]
}
}

extension UInt8 : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
return [self]
}
}

extension Double : BSONBytesProtocol {
internal func makeBytes() -> [UInt8] {
var integer = self
return withUnsafePointer(to: &integer) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Double>.size) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<Double>.size))
}
}
}
}

extension BSONBytesProtocol {
/// The bytes in `Self`
public var bytes : [UInt8] {
Expand Down
13 changes: 10 additions & 3 deletions Sources/ObjectId.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ public struct ObjectId {
var data = [UInt8]()

// Take the current UNIX epoch as Int32 and take it's bytes
data += Int32(currentTime.timeIntervalSince1970).bytes
data += Int32(currentTime.timeIntervalSince1970).makeBytes()

// Take a random number
data += ObjectId.random.bytes
data += ObjectId.random.makeBytes()

// And add a counter as 2 bytes and increment it
ObjectId.counterQueue.sync {
data += ObjectId.counter.bytes
data += ObjectId.counter.makeBytes()
ObjectId.counter = ObjectId.counter &+ 1
}

Expand Down Expand Up @@ -122,4 +122,11 @@ public struct ObjectId {
}
}

extension ObjectId: Hashable {
/// The hash value for this ObjectId is currently the hexstring. Will be more performant in the future
public var hashValue: Int {
return self.hexString.hashValue
}
}

private let radix16table: [UInt8] = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66]
12 changes: 6 additions & 6 deletions Sources/Value+Comparing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ extension Value : Equatable {}
public func ==(lhs: Value, rhs: Value) -> Bool {
switch (lhs, rhs) {
case (.double(_), _):
return lhs.double == rhs.double
return lhs.double == rhs.doubleValue
case (.string(_), _):
return lhs.string == rhs.string
return lhs.string == rhs.stringValue
case (.document(_), _), (.array(_), _):
return lhs.document == rhs.document
return lhs.document == rhs.documentValue && lhs.document.isArray == rhs.documentValue?.isArray
case (.binary(let subtype1, let data1), .binary(let subtype2, let data2)):
return subtype1.rawValue == subtype2.rawValue && data1 == data2
case (.objectId(_), .objectId(_)):
Expand All @@ -33,11 +33,11 @@ public func ==(lhs: Value, rhs: Value) -> Bool {
case (.javascriptCodeWithScope(let code1, let scope1), .javascriptCodeWithScope(let code2, let scope2)):
return code1 == code2 && scope1 == scope2
case (.int32(_), _):
return lhs.int32 == rhs.int32
return lhs.int32 == rhs.int32Value
case (.timestamp(let val1), .timestamp(let val2)):
return val1 == val2
case (.int64(_), _):
return lhs.int64 == rhs.int64
return lhs.int64 == rhs.int64Value
case (.minKey, .minKey), (.maxKey, .maxKey), (.null, .null), (.nothing, .nothing):
return true
default:
Expand Down Expand Up @@ -70,7 +70,7 @@ public func ==(lhs: Value, rhs: [UInt8]) -> Bool {
}

public func ==(lhs: Value, rhs: [Value]) -> Bool {
guard case .array(let document) = lhs, document.validatesAsArray() else {
guard case .array(let document) = lhs, document.validatesAsArray(), document.isArray else {
return false
}

Expand Down
Loading

0 comments on commit d0e63ea

Please sign in to comment.