blob: 380c6b2984bd475b2530308ec1540839ff338267 [file] [log] [blame]
/*
* Copyright 2024 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Foundation
enum FlexBuffersErrors: Error {
case sizeOfBufferIsTooSmall
case typeCouldNotBeDetermined
}
@inline(__always)
public func getRoot(buffer: ByteBuffer) throws -> Reference? {
let end = buffer.count
if buffer.count < 3 {
throw FlexBuffersErrors.sizeOfBufferIsTooSmall
}
let byteWidth = buffer.read(def: UInt8.self, position: end &- 1)
let packedType = buffer.read(def: UInt8.self, position: end &- 2)
let offset = end &- 2 &- numericCast(byteWidth)
return Reference(
byteBuffer: buffer,
offset: offset,
parentWidth: byteWidth,
packedType: packedType)
}
@inline(__always)
public func getRootChecked(buffer: ByteBuffer) throws -> Reference? {
// TODO(mustiikhalil): implement verifier
return try getRoot(buffer: buffer)
}
public struct Reference {
private let byteBuffer: ByteBuffer
private let offset: Int
private let parentWidth: UInt8
private let byteWidth: UInt8
public let type: FlexBufferType
@inline(__always)
init?(
byteBuffer: ByteBuffer,
offset: Int,
parentWidth: UInt8,
packedType: UInt8
) {
guard let type = FlexBufferType(rawValue: UInt64(packedType >> 2)) else {
return nil
}
self.byteBuffer = byteBuffer
self.offset = offset
self.parentWidth = parentWidth
byteWidth = 1 << (packedType & 3)
self.type = type
}
@inline(__always)
init(
byteBuffer: ByteBuffer,
offset: Int,
parentWidth: UInt8,
byteWidth: UInt8,
type: FlexBufferType
) {
self.byteBuffer = byteBuffer
self.offset = offset
self.parentWidth = parentWidth
self.byteWidth = byteWidth
self.type = type
}
@inline(__always)
public var bool: Bool? {
return switch type {
case .bool: byteBuffer.readUInt64(offset: offset, byteWidth: byteWidth) != 0
default: nil
}
}
@inline(__always)
public var uint: UInt64? {
return switch type {
case .uint: byteBuffer.readUInt64(offset: offset, byteWidth: byteWidth)
case .indirectUInt:
byteBuffer.readUInt64(
offset: indirect(),
byteWidth: byteWidth)
default: nil
}
}
@inline(__always)
public var int: Int64? {
return switch type {
case .int: byteBuffer.readInt64(offset: offset, byteWidth: byteWidth)
case .indirectInt:
byteBuffer.readInt64(
offset: indirect(),
byteWidth: byteWidth)
default: nil
}
}
@inline(__always)
public var double: Double? {
return switch type {
case .float: byteBuffer.readDouble(offset: offset, byteWidth: byteWidth)
case .indirectFloat:
byteBuffer.readDouble(
offset: indirect(),
byteWidth: byteWidth)
default: nil
}
}
@inline(__always)
public var map: Map? {
guard type == .map else { return nil }
return Map(
byteBuffer: byteBuffer,
offset: indirect(),
byteWidth: byteWidth)
}
@inline(__always)
public var vector: Vector? {
guard type == .vector || type == .map else { return nil }
return Vector(
byteBuffer: byteBuffer,
offset: indirect(),
byteWidth: byteWidth)
}
@inline(__always)
public var cString: String? {
guard type == .string || type == .key else { return nil }
let offset = indirect()
let count = getCount(
buffer: byteBuffer,
offset: offset,
byteWidth: byteWidth)
return byteBuffer.readString(
at: offset,
count: count)
}
@inline(__always)
public func blob<Result>(_ completion: (UnsafeRawBufferPointer) -> Result)
-> Result?
{
guard type == .blob || type == .string else { return nil }
let offset = indirect()
let count = getCount(
buffer: byteBuffer,
offset: offset,
byteWidth: byteWidth)
return byteBuffer.withUnsafePointerToSlice(
index: offset,
count: count,
body: completion)
}
@inline(__always)
public var typedVector: TypedVector? {
guard isTypedVectorType(type: type) else { return nil }
guard var type = toTypedVectorElementType(type: type) else { return nil }
if type == .string {
type = .key
}
return TypedVector(
byteBuffer: byteBuffer,
offset: indirect(),
byteWidth: byteWidth,
type: type)
}
@inline(__always)
public var fixedTypedVector: FixedTypedVector? {
guard isFixedTypedVectorType(type: type) else { return nil }
let t = toFixedTypedVectorElementType(type: type)
guard let type = t.type else { return nil }
return FixedTypedVector(
byteBuffer: byteBuffer,
offset: indirect(),
byteWidth: byteWidth,
type: type,
count: t.count)
}
@inline(__always)
public func string(encoding: String.Encoding = .utf8) -> String? {
guard type == .string else { return nil }
let offset = indirect()
let count = getCount(
buffer: byteBuffer,
offset: offset,
byteWidth: byteWidth)
return byteBuffer.readString(
at: offset,
count: count,
type: encoding)
}
@inline(__always)
public func asInt<T: FixedWidthInteger>() -> T? {
guard let v = int else {
return nil
}
return numericCast(v)
}
@inline(__always)
public func asUInt<T: FixedWidthInteger>() -> T? {
guard let v = uint else {
return nil
}
return numericCast(v)
}
@inline(__always)
public func withUnsafeRawPointer<Result>(
_ completion: (UnsafeRawPointer) throws
-> Result
)
rethrows -> Result?
{
return try byteBuffer.readWithUnsafeRawPointer(
position: indirect(),
completion)
}
private func indirect() -> Int {
readIndirect(buffer: byteBuffer, offset: offset, parentWidth)
}
}
extension Reference {
public func jsonString() -> String {
var str = ""
jsonBuilder(json: &str)
return str
}
func jsonBuilder(json: inout String) {
switch type {
case .null:
json += StaticJSON.null
case .uint, .indirectUInt:
json += uint.valueOrNull
case .int, .indirectInt:
json += int.valueOrNull
case .float, .indirectFloat:
json += double.valueOrNull
case .string, .key:
json += "\"\(cString ?? StaticJSON.null)\""
case .map:
map?.jsonBuilder(json: &json)
case .bool:
json += bool.valueOrNull
case .blob:
if let p = blob({ String(data: Data($0), encoding: .utf8) })?
.valueOrNull
{
json += "\"\(p)\""
} else {
json += StaticJSON.null
}
default:
if type == .vector {
vector?.jsonBuilder(json: &json)
} else if isTypedVectorType(type: type) {
typedVector?.jsonBuilder(json: &json)
} else if isFixedTypedVectorType(type: type) {
fixedTypedVector?.jsonBuilder(json: &json)
} else {
json += StaticJSON.null
}
}
}
}