11import CLibMongoC
22import Foundation
3+ import NIO
4+
5+ /// This shared allocator instance should be used for all underlying `ByteBuffer` creation.
6+ private let BSON_ALLOCATOR = ByteBufferAllocator ( )
37
48/// The possible types of BSON values and their corresponding integer values.
5- public enum BSONType : UInt32 {
9+ public enum BSONType : UInt8 {
610 /// An invalid type
711 case invalid = 0x00
812 /// 64-bit binary floating point
@@ -196,27 +200,61 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
196200 internal var bson : BSON { . binary( self ) }
197201
198202 /// The binary data.
199- public let data : Data
203+ public let data : ByteBuffer
200204
201205 /// The binary subtype for this data.
202- public let subtype : UInt8
206+ public let subtype : Subtype
203207
204208 /// Subtypes for BSON Binary values.
205- public enum Subtype : UInt8 {
209+ public struct Subtype : Equatable , Codable , Hashable , RawRepresentable {
210+ // swiftlint:disable force_unwrapping
206211 /// Generic binary subtype
207- case generic,
208- /// A function
209- function,
210- /// Binary (old)
211- binaryDeprecated,
212- /// UUID (old)
213- uuidDeprecated,
214- /// UUID (RFC 4122)
215- uuid,
216- /// MD5
217- md5,
218- /// User defined
219- userDefined = 0x80
212+ public static let generic = Subtype ( rawValue: 0x00 ) !
213+ /// A function
214+ public static let function = Subtype ( rawValue: 0x01 ) !
215+ /// Binary (old)
216+ public static let binaryDeprecated = Subtype ( rawValue: 0x02 ) !
217+ /// UUID (old)
218+ public static let uuidDeprecated = Subtype ( rawValue: 0x03 ) !
219+ /// UUID (RFC 4122)
220+ public static let uuid = Subtype ( rawValue: 0x04 ) !
221+ /// MD5
222+ public static let md5 = Subtype ( rawValue: 0x05 ) !
223+ /// Encrypted BSON value
224+ public static let encryptedValue = Subtype ( rawValue: 0x06 ) !
225+ // swiftlint:enable force_unwrapping
226+
227+ /// Subtype indicator value
228+ public let rawValue : UInt8
229+
230+ /// Initializes a `Subtype` with a custom value.
231+ /// Returns nil if rawValue within reserved range [0x07, 0x80).
232+ public init ? ( rawValue: UInt8 ) {
233+ guard !( rawValue > 0x06 && rawValue < 0x80 ) else {
234+ return nil
235+ }
236+ self . rawValue = rawValue
237+ }
238+
239+ internal init ( _ value: bson_subtype_t ) { self . rawValue = UInt8 ( value. rawValue) }
240+
241+ /// Initializes a `Subtype` with a custom value. This value must be in the range 0x80-0xFF.
242+ /// - Throws:
243+ /// - `InvalidArgumentError` if value passed is outside of the range 0x80-0xFF
244+ public static func userDefined( _ value: Int ) throws -> Subtype {
245+ guard let byteValue = UInt8 ( exactly: value) else {
246+ throw InvalidArgumentError ( message: " Cannot represent \( value) as UInt8 " )
247+ }
248+ guard byteValue >= 0x80 else {
249+ throw InvalidArgumentError (
250+ message: " userDefined value must be greater than or equal to 0x80 got \( byteValue) "
251+ )
252+ }
253+ guard let subtype = Subtype ( rawValue: byteValue) else {
254+ throw InvalidArgumentError ( message: " Cannot represent \( byteValue) as Subtype " )
255+ }
256+ return subtype
257+ }
220258 }
221259
222260 /// Initializes a `BSONBinary` instance from a `UUID`.
@@ -238,29 +276,24 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
238276 /// Initializes a `BSONBinary` instance from a `Data` object and a `UInt8` subtype.
239277 /// - Throws:
240278 /// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype.
241- public init ( data: Data , subtype: UInt8 ) throws {
242- if [ Subtype . uuid. rawValue , Subtype . uuidDeprecated. rawValue ] . contains ( subtype) && data. count != 16 {
279+ public init ( data: Data , subtype: Subtype ) throws {
280+ if [ Subtype . uuid, Subtype . uuidDeprecated] . contains ( subtype) && data. count != 16 {
243281 throw InvalidArgumentError (
244282 message:
245283 " Binary data with UUID subtype must be 16 bytes, but data has \( data. count) bytes "
246284 )
247285 }
248286 self . subtype = subtype
249- self . data = data
250- }
251-
252- /// Initializes a `BSONBinary` instance from a `Data` object and a `Subtype`.
253- /// - Throws:
254- /// - `InvalidArgumentError` if the provided data is incompatible with the specified subtype.
255- public init ( data: Data , subtype: Subtype ) throws {
256- try self . init ( data: data, subtype: subtype. rawValue)
287+ var buffer = BSON_ALLOCATOR . buffer ( capacity: data. count)
288+ buffer. writeBytes ( data)
289+ self . data = buffer
257290 }
258291
259- /// Initializes a `BSONBinary` instance from a base64 `String` and a `UInt8` subtype .
292+ /// Initializes a `BSONBinary` instance from a base64 `String` and a `Subtype` .
260293 /// - Throws:
261294 /// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is
262295 /// incompatible with the specified subtype.
263- public init ( base64: String , subtype: UInt8 ) throws {
296+ public init ( base64: String , subtype: Subtype ) throws {
264297 guard let dataObj = Data ( base64Encoded: base64) else {
265298 throw InvalidArgumentError (
266299 message:
@@ -270,14 +303,6 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
270303 try self . init ( data: dataObj, subtype: subtype)
271304 }
272305
273- /// Initializes a `BSONBinary` instance from a base64 `String` and a `Subtype`.
274- /// - Throws:
275- /// - `InvalidArgumentError` if the base64 `String` is invalid or if the provided data is
276- /// incompatible with the specified subtype.
277- public init ( base64: String , subtype: Subtype ) throws {
278- try self . init ( base64: base64, subtype: subtype. rawValue)
279- }
280-
281306 public init ( from decoder: Decoder ) throws {
282307 throw getDecodingError ( type: BSONBinary . self, decoder: decoder)
283308 }
@@ -287,9 +312,11 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
287312 }
288313
289314 internal func encode( to document: inout BSONDocument , forKey key: String ) throws {
290- let subtype = bson_subtype_t ( UInt32 ( self . subtype) )
291- let length = self . data. count
292- let byteArray = [ UInt8] ( self . data)
315+ let subtype = bson_subtype_t ( UInt32 ( self . subtype. rawValue) )
316+ let length = self . data. writerIndex
317+ guard let byteArray = self . data. getBytes ( at: 0 , length: length) else {
318+ throw InternalError ( message: " Cannot read \( length) bytes from Binary.data " )
319+ }
293320 try document. withMutableBSONPointer { docPtr in
294321 guard bson_append_binary ( docPtr, key, Int32 ( key. utf8. count) , subtype, byteArray, UInt32 ( length) ) else {
295322 throw bsonTooLargeError ( value: self , forKey: key)
@@ -318,21 +345,24 @@ public struct BSONBinary: BSONValue, Equatable, Codable, Hashable {
318345 }
319346
320347 let dataObj = Data ( bytes: data, count: Int ( length) )
321- return try self . init ( data: dataObj, subtype: UInt8 ( subtype. rawValue ) )
348+ return try self . init ( data: dataObj, subtype: Subtype ( subtype) )
322349 } )
323350 }
324351
325352 /// Converts this `BSONBinary` instance to a `UUID`.
326353 /// - Throws:
327354 /// - `InvalidArgumentError` if a non-UUID subtype is set on this `BSONBinary`.
328355 public func toUUID( ) throws -> UUID {
329- guard [ Subtype . uuid. rawValue , Subtype . uuidDeprecated. rawValue ] . contains ( self . subtype) else {
356+ guard [ Subtype . uuid, Subtype . uuidDeprecated] . contains ( self . subtype) else {
330357 throw InvalidArgumentError (
331358 message: " Expected a UUID binary subtype, got subtype \( self . subtype) instead. "
332359 )
333360 }
334361
335- let data = self . data
362+ guard let data = self . data. getBytes ( at: 0 , length: 16 ) else {
363+ throw InternalError ( message: " Unable to read 16 bytes from Binary.data " )
364+ }
365+
336366 let uuid : uuid_t = (
337367 data [ 0 ] , data [ 1 ] , data [ 2 ] , data [ 3 ] ,
338368 data [ 4 ] , data [ 5 ] , data [ 6 ] , data [ 7 ] ,
@@ -487,16 +517,20 @@ public struct BSONDecimal128: BSONValue, Equatable, Codable, CustomStringConvert
487517 self . decimal128 = bsonDecimal
488518 }
489519
490- /// Initializes a `BSONDecimal128` value from the provided `String`. Returns `nil` if the input is not a valid
491- /// Decimal128 string.
492- /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst
493- public init ? ( _ data: String ) {
494- do {
495- let bsonType = try BSONDecimal128 . toLibBSONType ( data)
496- self . init ( bsonDecimal: bsonType)
497- } catch {
498- return nil
499- }
520+ /**
521+ * Initializes a `BSONDecimal128` value from the provided `String`.
522+ *
523+ * - Parameters:
524+ * - a BSONDecimal128 number as a string.
525+ *
526+ * - Throws:
527+ * - A `InvalidArgumentError` if the string does not represent a BSONDecimal128 encodable value.
528+ *
529+ * - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst
530+ */
531+ public init ( _ data: String ) throws {
532+ let bsonType = try BSONDecimal128 . toLibBSONType ( data)
533+ self . init ( bsonDecimal: bsonType)
500534 }
501535
502536 public init ( from decoder: Decoder ) throws {
@@ -731,7 +765,7 @@ public struct BSONCode: BSONValue, Equatable, Codable, Hashable {
731765}
732766
733767/// A struct to represent the BSON MaxKey type.
734- internal struct MaxKey : BSONValue , Equatable , Codable , Hashable {
768+ internal struct BSONMaxKey : BSONValue , Equatable , Codable , Hashable {
735769 internal var bson : BSON { . maxKey }
736770
737771 internal static var bsonType : BSONType { . maxKey }
@@ -748,7 +782,7 @@ internal struct MaxKey: BSONValue, Equatable, Codable, Hashable {
748782 internal init ( ) { }
749783
750784 internal init ( from decoder: Decoder ) throws {
751- throw getDecodingError ( type: MaxKey . self, decoder: decoder)
785+ throw getDecodingError ( type: BSONMaxKey . self, decoder: decoder)
752786 }
753787
754788 internal func encode( to: Encoder ) throws {
@@ -757,7 +791,7 @@ internal struct MaxKey: BSONValue, Equatable, Codable, Hashable {
757791
758792 internal static func from( iterator iter: BSONDocumentIterator ) throws -> BSON {
759793 guard iter. currentType == . maxKey else {
760- throw wrongIterTypeError ( iter, expected: MaxKey . self)
794+ throw wrongIterTypeError ( iter, expected: BSONMaxKey . self)
761795 }
762796 return . maxKey
763797 }
@@ -813,11 +847,6 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab
813847 }
814848 }
815849
816- /// The timestamp used to create this `BSONObjectID`
817- public var timestamp : UInt32 {
818- withUnsafePointer ( to: self . oid) { oidPtr in UInt32 ( bson_oid_get_time_t ( oidPtr) ) }
819- }
820-
821850 public var description : String {
822851 self . hex
823852 }
@@ -831,12 +860,13 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab
831860 self . oid = oid
832861 }
833862
834- /// Initializes an `BSONObjectID` from the provided hex `String`. Returns `nil` if the string is not a valid
835- /// ObjectID.
863+ /// Initializes an `BSONObjectID` from the provided hex `String`.
864+ /// - Throws:
865+ /// - `InvalidArgumentError` if string passed is not a valid BSONObjectID
836866 /// - SeeAlso: https://github.com/mongodb/specifications/blob/master/source/objectid.rst
837- public init ? ( _ hex: String ) {
867+ public init ( _ hex: String ) throws {
838868 guard bson_oid_is_valid ( hex, hex. utf8. count) else {
839- return nil
869+ throw InvalidArgumentError ( message : " Cannot create ObjectId from \( hex ) " )
840870 }
841871 var oid_t = bson_oid_t ( )
842872 bson_oid_init_from_string ( & oid_t, hex)
@@ -851,7 +881,7 @@ public struct BSONObjectID: BSONValue, Equatable, CustomStringConvertible, Codab
851881 // assumes that the BSONObjectID is stored as a valid hex string.
852882 let container = try decoder. singleValueContainer ( )
853883 let hex = try container. decode ( String . self)
854- guard let oid = BSONObjectID ( hex) else {
884+ guard let oid = try ? BSONObjectID ( hex) else {
855885 throw DecodingError . dataCorrupted (
856886 DecodingError . Context (
857887 codingPath: decoder. codingPath,
0 commit comments