Skip to content

Commit cbb9662

Browse files
authored
SWIFT-1072 Improve insertion performance (#55)
1 parent ac0e0ee commit cbb9662

File tree

6 files changed

+224
-119
lines changed

6 files changed

+224
-119
lines changed

Sources/SwiftBSON/BSONDocument.swift

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,27 @@ public struct BSONDocument {
7474
* - SeeAlso: http://bsonspec.org/
7575
*/
7676
public init(fromBSON bson: ByteBuffer) throws {
77-
let storage = BSONDocumentStorage(bson)
77+
let storage = BSONDocumentStorage(bson.slice())
7878
try storage.validate()
79-
self = BSONDocument(fromUnsafeBSON: storage)
79+
self.storage = storage
80+
}
81+
82+
/**
83+
* Initialize a new `BSONDocument` from the provided BSON data without validating the elements. The first four
84+
* bytes must accurately reflect the length of the buffer, however.
85+
*
86+
* If invalid BSON data is provided, undefined behavior or server-side errors may occur when using the
87+
* resultant `BSONDocument`.
88+
*
89+
* - Throws: `BSONError.InvalidArgumentError` if the provided BSON's length does not match the encoded length.
90+
*/
91+
public init(fromBSONWithoutValidatingElements bson: ByteBuffer) throws {
92+
let storage = BSONDocumentStorage(bson)
93+
try self.init(fromBSONWithoutValidatingElements: storage)
8094
}
8195

82-
internal init(fromUnsafeBSON storage: BSONDocumentStorage) {
96+
internal init(fromBSONWithoutValidatingElements storage: BSONDocumentStorage) throws {
97+
try storage.validateLength()
8398
self.storage = storage
8499
}
85100

@@ -129,23 +144,15 @@ public struct BSONDocument {
129144

130145
/// The keys in this `BSONDocument`.
131146
public var keys: [String] {
132-
do {
133-
return try BSONDocumentIterator.getKeys(from: self.storage.buffer)
134-
} catch {
135-
fatalError("Failed to retrieve keys for document")
136-
}
147+
BSONDocumentIterator.getKeys(from: self.storage.buffer)
137148
}
138149

139150
/// The values in this `BSONDocument`.
140151
public var values: [BSON] { self.map { _, val in val } }
141152

142153
/// The number of (key, value) pairs stored at the top level of this document.
143154
public var count: Int {
144-
do {
145-
return try BSONDocumentIterator.getKeys(from: self.storage.buffer).count
146-
} catch {
147-
return 0
148-
}
155+
BSONDocumentIterator.getKeys(from: self.storage.buffer).count
149156
}
150157

151158
/// A copy of the `ByteBuffer` backing this document, containing raw BSON data. As `ByteBuffer`s implement
@@ -158,7 +165,8 @@ public struct BSONDocument {
158165

159166
/// Returns a `Boolean` indicating whether this `BSONDocument` contains the provided key.
160167
public func hasKey(_ key: String) -> Bool {
161-
(try? BSONDocumentIterator.find(key: key, in: self)) != nil
168+
let it = self.makeIterator()
169+
return it.findValue(forKey: key) != nil
162170
}
163171

164172
/**
@@ -174,19 +182,10 @@ public struct BSONDocument {
174182
*/
175183
public subscript(key: String) -> BSON? {
176184
get {
177-
do {
178-
return try BSONDocumentIterator.find(key: key, in: self)?.value
179-
} catch {
180-
fatalError("Error looking up key \(key) in document: \(error)")
181-
}
185+
BSONDocumentIterator.find(key: key, in: self)?.value
182186
}
183187
set {
184-
// The only time this would crash is document too big error
185-
do {
186-
return try self.set(key: key, to: newValue)
187-
} catch {
188-
fatalError("Failed to set \(key) to \(String(describing: newValue)): \(error)")
189-
}
188+
self.set(key: key, to: newValue)
190189
}
191190
}
192191

@@ -251,10 +250,9 @@ public struct BSONDocument {
251250
)
252251
}
253252
newStorage.buffer.writeBytes(suffix)
253+
newStorage.encodedLength = newSize
254254

255-
var document = BSONDocument(fromUnsafeBSON: newStorage)
256-
document.storage.encodedLength = newSize
257-
return document
255+
return try BSONDocument(fromBSONWithoutValidatingElements: newStorage)
258256
}
259257

260258
/// Appends the provided key value pair without checking to see if the key already exists.
@@ -271,8 +269,8 @@ public struct BSONDocument {
271269
* Sets a BSON element with the corresponding key
272270
* if element.value is nil the element is deleted from the BSON
273271
*/
274-
internal mutating func set(key: String, to value: BSON?) throws {
275-
guard let range = try BSONDocumentIterator.findByteRange(for: key, in: self) else {
272+
private mutating func set(key: String, to value: BSON?) {
273+
guard let range = BSONDocumentIterator.findByteRange(for: key, in: self) else {
276274
guard let value = value else {
277275
// no-op: key does not exist and the value is nil
278276
return
@@ -285,18 +283,18 @@ public struct BSONDocument {
285283
let suffixLength = self.storage.encodedLength - range.endIndex
286284

287285
guard
288-
let prefix = self.storage.buffer.getBytes(at: 0, length: prefixLength),
286+
var prefix = self.storage.buffer.getSlice(at: 0, length: prefixLength),
289287
let suffix = self.storage.buffer.getBytes(at: range.endIndex, length: suffixLength)
290288
else {
291-
throw BSONError.InternalError(
292-
message: "Cannot slice buffer from " +
289+
fatalError(
290+
"Cannot slice buffer from " +
293291
"0 to len \(range.startIndex) and from \(range.endIndex) " +
294292
"to len \(suffixLength) : \(self.storage.buffer)"
295293
)
296294
}
297295

298296
var newStorage = BSONDocumentStorage()
299-
newStorage.buffer.writeBytes(prefix)
297+
newStorage.buffer.writeBuffer(&prefix)
300298

301299
var newSize = self.storage.encodedLength - (range.endIndex - range.startIndex)
302300
if let value = value {
@@ -305,7 +303,7 @@ public struct BSONDocument {
305303
newSize += size
306304

307305
guard newSize <= BSON_MAX_SIZE else {
308-
throw BSONError.DocumentTooLargeError(value: value.bsonValue, forKey: key)
306+
fatalError(BSONError.DocumentTooLargeError(value: value.bsonValue, forKey: key).message)
309307
}
310308
}
311309

@@ -389,8 +387,12 @@ public struct BSONDocument {
389387
return totalBytes
390388
}
391389

392-
internal func validate() throws {
393-
// Pull apart the underlying binary into [KeyValuePair], should reveal issues
390+
/// Verify that the encoded length matches the actual length of the buffer and that the buffer is
391+
/// isn't too small or too large.
392+
///
393+
/// - Throws: `BSONError.InvalidArgumentError` if validation fails
394+
///
395+
internal func validateLength() throws {
394396
guard let encodedLength = self.buffer.getInteger(at: 0, endianness: .little, as: Int32.self) else {
395397
throw BSONError.InvalidArgumentError(message: "Validation Failed: Cannot read encoded length")
396398
}
@@ -403,11 +405,16 @@ public struct BSONDocument {
403405

404406
guard encodedLength == self.buffer.readableBytes else {
405407
throw BSONError.InvalidArgumentError(
406-
message: "BSONDocument's encoded byte length is \(encodedLength), however the" +
408+
message: "BSONDocument's encoded byte length is \(encodedLength), however the " +
407409
"buffer has \(self.buffer.readableBytes) readable bytes"
408410
)
409411
}
412+
}
410413

414+
internal func validate() throws {
415+
try self.validateLength()
416+
417+
// Pull apart the underlying binary into [KeyValuePair], should reveal issues
411418
var keySet = Set<String>()
412419
let iter = BSONDocumentIterator(over: self.buffer)
413420
do {
@@ -528,7 +535,7 @@ extension BSONDocument: BSONValue {
528535
throw BSONError.InternalError(message: "Cannot read document contents")
529536
}
530537

531-
return .document(BSONDocument(fromUnsafeBSON: BSONDocument.BSONDocumentStorage(bytes)))
538+
return .document(try BSONDocument(fromBSONWithoutValidatingElements: BSONDocument.BSONDocumentStorage(bytes)))
532539
}
533540

534541
internal func write(to buffer: inout ByteBuffer) {

0 commit comments

Comments
 (0)