Skip to content

Commit

Permalink
Implement remaining unimplemented pieces of code
Browse files Browse the repository at this point in the history
  • Loading branch information
Joannis committed Oct 28, 2018
1 parent fc83d26 commit 9e8893d
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 82 deletions.
42 changes: 27 additions & 15 deletions Sources/BSON/Codable/Decoding/BSONDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,16 @@ extension BSONDecoderSettings.FloatDecodingStrategy {
/// Decodes the `value` with a key of `key` to a `Float` using the current strategy
internal func decode<K: CodingKey>(fromKey key: K, in value: DecoderValue, path: [String]) throws -> Float {
switch self {
case .double:
return try Float(value.unwrap(asType: Double.self, atKey: key, path: path))
case .double, .adaptive:
do {
return try Float(value.unwrap(asType: Double.self, atKey: key, path: path))
} catch {
guard case .adaptive = self else {
throw error
}

fallthrough // if adaptive
}
case .string:
let string = try value.unwrap(asType: String.self, atKey: key, path: path)

Expand All @@ -30,8 +38,6 @@ extension BSONDecoderSettings.FloatDecodingStrategy {
}

return float
case .adaptive:
unimplemented()
case .custom(let strategy):
guard
case .document(let document) = value,
Expand All @@ -47,9 +53,18 @@ extension BSONDecoderSettings.FloatDecodingStrategy {
/// Decodes the `value` without key to a `Float` using the current strategy
internal func decode(from value: DecoderValue, path: [String]) throws -> Float {
switch self {
case .double:
let double = try value.unwrap(asType: Double.self, path: path)
return Float(double)
case .double, .adaptive:
do {
let double = try value.unwrap(asType: Double.self, path: path)

return Float(double)
} catch {
guard case .adaptive = self else {
throw error
}

fallthrough // if adaptive
}
case .string:
let string = try value.unwrap(asType: String.self, path: path)

Expand All @@ -58,8 +73,6 @@ extension BSONDecoderSettings.FloatDecodingStrategy {
}

return float
case .adaptive:
unimplemented()
case .custom(let strategy):
guard let float = try strategy(nil, value.primitive) else {
throw BSONValueNotFound(type: Float.self, path: path)
Expand Down Expand Up @@ -178,7 +191,7 @@ extension BSONDecoderSettings.DoubleDecodingStrategy {
internal func decode(primitive: Primitive, path: @autoclosure () -> [String]) throws -> Double {
switch (primitive, self) {
case (let string as String, .textual), (let string as String, .adaptive):
guard let double = try Double(string) else {
guard let double = Double(string) else {
throw BSONValueNotFound(type: Double.self, path: path())
}

Expand Down Expand Up @@ -223,10 +236,9 @@ extension BSONDecoderSettings.DoubleDecodingStrategy {
return double
default:
guard
let primitive = decoder.document?[key.stringValue],
let identifier = decoder.document?.typeIdentifier(of: key.stringValue)
else {
throw BSONValueNotFound(type: Double.self, path: path())
let primitive = decoder.document?[key.stringValue]
else {
throw BSONValueNotFound(type: Double.self, path: path())
}

return try decode(primitive: primitive, path: path)
Expand Down Expand Up @@ -342,7 +354,7 @@ internal struct _BSONDecoder: Decoder {
switch value {
case let string as String:
return string
case let double as String:
case let double as Double:
return double.description
case let int as Int32:
return int.description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ internal struct KeyedBSONDecodingContainer<K: CodingKey>: KeyedDecodingContainer
} else {
guard
let value = self.document[key.stringValue]
else {
throw BSONValueNotFound(type: T.self, path: path(forKey: key))
else {
throw BSONValueNotFound(type: T.self, path: path(forKey: key))
}

// Decoding strategy for Primitives, like Date
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ internal struct SingleValueBSONDecodingContainer: SingleValueDecodingContainer,
return binary
}

func decodeDecimal128() throws -> Decimal128 {
guard let decimal128 = self.decoder.primitive as? Decimal128 else {
throw BSONValueNotFound(type: Decimal128.self, path: self.codingPath.path)
}

return decimal128
}

func decodeObjectId() throws -> ObjectId {
guard let objectId = self.decoder.primitive as? ObjectId else {
throw BSONValueNotFound(type: ObjectId.self, path: self.codingPath.path)
Expand Down
100 changes: 63 additions & 37 deletions Sources/BSON/Document/Document+Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ extension Document {

func valueLength(forType type: TypeIdentifier, at offset: Int) -> Int? {
switch type {
case .string, .javascript:
case .string, .javascript: // Int32 is excluding the int32 header
guard let binaryLength = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self) else {
return nil
}

return numericCast(4 &+ binaryLength)
case .document, .array:
case .document, .array, .javascriptWithScope: // Int32 is including the int32 header
guard let documentLength = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self) else {
return nil
}
Expand Down Expand Up @@ -192,16 +192,6 @@ extension Document {
}

return patternEndOffset + optionsEndOffset
case .javascriptWithScope:
guard let string = valueLength(forType: .string, at: offset) else {
return nil
}

guard let document = valueLength(forType: .document, at: offset) else {
return nil
}

return string &+ document
case .int32:
return 4
case .decimal128:
Expand Down Expand Up @@ -294,33 +284,38 @@ extension Document {
switch type {
case .double:
return self.storage.getDouble(at: offset)
case .string, .binary, .document, .array:
case .string:
guard let length = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self) else {
return nil
}

if type == .string {
return self.storage.getString(at: offset &+ 4, length: numericCast(length) - 1)
} else if type == .document || type == .array {
guard let slice = self.storage.getSlice(at: offset, length: numericCast(length)) else {
return nil
}

return Document(
storage: slice,
cache: DocumentCache(),
isArray: type == .array
)
} else {
guard
let subType = self.storage.getByte(at: offset &+ 4),
let slice = self.storage.getSlice(at: offset &+ 5, length: numericCast(length))
else {
return nil
}

return Binary(subType: Binary.SubType(subType), buffer: slice)
return self.storage.getString(at: offset &+ 4, length: numericCast(length) - 1)
case .binary:
guard let length = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self) else {
return nil
}

guard
let subType = self.storage.getByte(at: offset &+ 4),
let slice = self.storage.getSlice(at: offset &+ 5, length: numericCast(length))
else {
return nil
}

return Binary(subType: Binary.SubType(subType), buffer: slice)
case .document, .array:
guard
let length = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self),
let slice = self.storage.getSlice(at: offset, length: numericCast(length))
else {
return nil
}

return Document(
storage: slice,
cache: DocumentCache(),
isArray: type == .array
)
case .objectId:
guard let slice = storage.getBytes(at: offset, length: 12) else {
return nil
Expand Down Expand Up @@ -369,13 +364,44 @@ extension Document {

return RegularExpression(pattern: pattern, options: options)
case .javascript:
unimplemented()
guard
let length = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self),
let code = self.storage.getString(at: offset &+ 4, length: numericCast(length) - 1)
else {
return nil
}

return JavaScriptCode(code)
case .javascriptWithScope:
unimplemented()
guard let length = self.storage.getInteger(at: offset, endianness: .little, as: Int32.self) else {
return nil
}

guard
let codeLength = self.storage.getInteger(at: offset &+ 4, endianness: .little, as: Int32.self),
let code = self.storage.getString(at: offset &+ 8, length: numericCast(length) - 1)
else {
return nil
}

guard
let documentLength = self.storage.getInteger(at: offset &+ 8 &+ numericCast(codeLength), endianness: .little, as: Int32.self),
let slice = self.storage.getSlice(at: offset, length: numericCast(documentLength))
else {
return nil
}

let scope = Document(
storage: slice,
cache: DocumentCache(),
isArray: false
)

return JavaScriptCodeWithScope(code, scope: scope)
case .int32:
return self.storage.getInteger(at: offset, endianness: .little, as: Int32.self)
case .decimal128:
guard let slice = storage.getSlice(at: offset, length: 16) else {
guard let slice = storage.getBytes(at: offset, length: 16) else {
return nil
}

Expand Down
23 changes: 21 additions & 2 deletions Sources/BSON/Document/Document+Mutations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,31 @@ extension Document {
storage.write(integer: int, endianness: .little)
case let decimal128 as Decimal128:
prepareWritingPrimitive(.decimal128, bodyLength: 16, existingDimensions: dimensions, key: key)
var buffer = decimal128.storage
storage.write(buffer: &buffer)
storage.write(bytes: decimal128.storage)
case is MaxKey: // 0x7F
prepareWritingPrimitive(.maxKey, bodyLength: 0, existingDimensions: dimensions, key: key)
case is MinKey: // 0xFF
prepareWritingPrimitive(.maxKey, bodyLength: 0, existingDimensions: dimensions, key: key)
case let javascript as JavaScriptCode:
let codeLengthWithNull = javascript.code.utf8.count + 1
prepareWritingPrimitive(.javascript, bodyLength: 4 + codeLengthWithNull, existingDimensions: dimensions, key: key)

storage.write(integer: Int32(codeLengthWithNull), endianness: .little)
storage.write(string: javascript.code)
storage.write(integer: 0, endianness: .little, as: UInt8.self)
case let javascript as JavaScriptCodeWithScope:
var codeBuffer = javascript.scope.makeByteBuffer()

let codeLength = javascript.code.utf8.count + 1 // code, null terminator
let codeLengthWithHeader = 4 + codeLength
let primitiveLength = 4 + codeLengthWithHeader + codeBuffer.writerIndex // int32(code_w_s size), code, scope doc

prepareWritingPrimitive(.javascriptWithScope, bodyLength: primitiveLength, existingDimensions: dimensions, key: key)

storage.write(integer: Int32(primitiveLength), endianness: .little) // header
storage.write(integer: codeLength) // string (code)
storage.write(string: javascript.code)
storage.write(buffer: &codeBuffer)
default:
guard let data = primitive as? BSONDataType else {
assertionFailure("Currently unsupported type \(primitive)")
Expand Down
6 changes: 0 additions & 6 deletions Sources/BSON/Document/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ import NIO
#error("BSON does not support 32-bit platforms, PRs are welcome 🎉🐈")
#endif

// TODO: Remove when unused
@available(*, deprecated, message: "Unimplemented methods should be implemented")
func unimplemented(_ function: String = #function) -> Never {
fatalError("\(function) is unimplemented")
}

public struct Document: Primitive {
static let allocator = ByteBufferAllocator()

Expand Down
28 changes: 23 additions & 5 deletions Sources/BSON/Types/Decimal128.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,37 @@ import NIO
///
/// OpenKitten BSON currently does not support the handling of Decimal128 values. The type is a stub and provides no API. It serves as a point for future implementation.
public struct Decimal128: Primitive {
var storage: ByteBuffer
var storage: [UInt8]

internal init(_ storage: ByteBuffer) {
Swift.assert(storage.readableBytes == 16)
internal init(_ storage: [UInt8]) {
Swift.assert(storage.count == 16)

self.storage = storage
}

public func encode(to encoder: Encoder) throws {
unimplemented()
let container = encoder.singleValueContainer()

if var container = container as? AnySingleValueBSONEncodingContainer {
try container.encode(primitive: self)
} else {
throw UnsupportedDecimal128()
}
}

public init(from decoder: Decoder) throws {
unimplemented()
let container = try decoder.singleValueContainer()

if let container = container as? AnySingleValueBSONDecodingContainer {
self = try container.decodeDecimal128()
} else {
throw UnsupportedDecimal128()
}
}
}

fileprivate struct UnsupportedDecimal128: Error {
init() {}

let message = "Decimal128 did not yet implement Codable using non-BSON encoders"
}
21 changes: 21 additions & 0 deletions Sources/BSON/Types/JavaScript.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
public struct JavaScriptCode: Primitive, ExpressibleByStringLiteral {
public var code: String

public init(_ code: String) {
self.code = code
}

public init(stringLiteral value: String) {
self.code = value
}
}

public struct JavaScriptCodeWithScope: Primitive {
public var code: String
public var scope: Document

public init(_ code: String, scope: Document) {
self.code = code
self.scope = scope
}
}
1 change: 1 addition & 0 deletions Sources/BSON/Types/Primitives.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal protocol AnyBSONEncoder {
internal protocol AnySingleValueBSONDecodingContainer {
func decodeObjectId() throws -> ObjectId
func decodeDocument() throws -> Document
func decodeDecimal128() throws -> Decimal128
func decodeBinary() throws -> Binary
func decodeRegularExpression() throws -> RegularExpression
func decodeNull() throws -> Null
Expand Down
Loading

0 comments on commit 9e8893d

Please sign in to comment.