/* * 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 Common import Foundation enum FlexBuffersErrors: Error { case sizeOfBufferIsTooSmall case typeCouldNotBeDetermined } @inline(__always) public func getRoot(buffer: ByteBuffer) throws -> Reference? { assert( isLitteEndian, "Swift FlexBuffers currently only supports little-endian systems") 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(_ 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? { guard let v = int else { return nil } return numericCast(v) } @inline(__always) public func asUInt() -> T? { guard let v = uint else { return nil } return numericCast(v) } @inline(__always) public func withUnsafeRawPointer( _ 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 } } } }