forked from BigfootDev/flatbuffers
Remove custom flags for native arrays when using flexbuffers on Wasm Co-authored-by: Wouter van Oortmerssen <aardappel@gmail.com>
307 lines
7.4 KiB
Swift
307 lines
7.4 KiB
Swift
/*
|
|
* 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<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
|
|
}
|
|
}
|
|
}
|
|
}
|