Skip to content

Commit 40708c5

Browse files
committed
Reading a flexbuffer
Implementing reading from a flexbuffer, enabling most of the buffers features, like most types, maps, vectors, typedvectors, and fixedtypedvectors. Currently, if an offset/object cant be read we default to a swift nil instead of the default flexbuffers 'null' with all values.
1 parent 44dfdca commit 40708c5

21 files changed

+1092
-109
lines changed

swift.swiftformat

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
--exclude **/*_generated.swift
2424
--exclude **/swift_code_*.swift
2525
--exclude **/*.grpc.swift
26+
--exclude **/Build/tests/**
2627

27-
--header "/*\n * Copyright 2024 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */"
28+
--header "/*\n * Copyright 2024 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */"

swift/Sources/Common/Constants.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ extension Int64: Scalar {
8686
public typealias NumericValue = Int64
8787
}
8888

89+
extension UInt: Scalar {
90+
public typealias NumericValue = UInt
91+
}
92+
8993
extension UInt8: Scalar {
9094
public typealias NumericValue = UInt8
9195
}

swift/Sources/FlexBuffers/ByteBuffer.swift

Lines changed: 83 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ public struct ByteBuffer {
178178

179179
/// The size of the elements written to the buffer + their paddings
180180
private var _readerIndex: Int = 0
181-
/// Reader is the position of the current Writer Index (capacity - size)
182-
public var reader: Int { _storage.capacity &- _readerIndex }
181+
// /// Reader is the position of the current Writer Index (capacity - size)
182+
// var reader: Int { _storage.capacity &- _readerIndex }
183183
/// Current size of the buffer
184-
public var size: Int { _readerIndex }
185-
/// Current capacity for the buffer
184+
public var count: Int { _readerIndex }
185+
/// Current capacity for the buffer including unused space
186186
public var capacity: Int { _storage.capacity }
187187

188188
/// Constructor that creates a Flatbuffer object from an InternalByteBuffer
@@ -315,6 +315,68 @@ public struct ByteBuffer {
315315
}
316316
}
317317

318+
@inline(__always)
319+
public func readUInt64(offset: Int, byteWidth: UInt8) -> UInt64 {
320+
readSizedScalar(
321+
def: UInt64.self,
322+
t1: UInt8.self,
323+
t2: UInt16.self,
324+
t3: UInt32.self,
325+
t4: UInt64.self,
326+
position: offset,
327+
byteWidth: byteWidth)
328+
}
329+
330+
@inline(__always)
331+
public func readInt64(offset: Int, byteWidth: UInt8) -> Int64 {
332+
readSizedScalar(
333+
def: Int64.self,
334+
t1: Int8.self,
335+
t2: Int16.self,
336+
t3: Int32.self,
337+
t4: Int64.self,
338+
position: offset,
339+
byteWidth: byteWidth)
340+
}
341+
342+
@inline(__always)
343+
public func readDouble(offset: Int, byteWidth: UInt8) -> Double {
344+
switch byteWidth {
345+
case 4:
346+
Double(read(def: Float32.self, position: offset))
347+
default:
348+
read(def: Double.self, position: offset)
349+
}
350+
}
351+
352+
@inline(__always)
353+
func readSizedScalar<
354+
T: BinaryInteger,
355+
T1: BinaryInteger,
356+
T2: BinaryInteger,
357+
T3: BinaryInteger,
358+
T4: BinaryInteger
359+
>(
360+
def: T.Type,
361+
t1: T1.Type,
362+
t2: T2.Type,
363+
t3: T3.Type,
364+
t4: T4.Type,
365+
position: Int,
366+
byteWidth: UInt8) -> T
367+
{
368+
switch byteWidth {
369+
case 1:
370+
numericCast(read(def: T1.self, position: position))
371+
case 2:
372+
numericCast(read(def: T2.self, position: position))
373+
case 4:
374+
numericCast(read(def: T3.self, position: position))
375+
default:
376+
numericCast(read(def: T4.self, position: position))
377+
}
378+
}
379+
318380
/// Reads a slice from the memory assuming a type of T
319381
/// - Parameters:
320382
/// - index: index of the object to be read from the buffer
@@ -336,36 +398,11 @@ public struct ByteBuffer {
336398
}
337399
}
338400

339-
/// Provides a pointer towards the underlying primitive types
340-
/// - Parameters:
341-
/// - index: index of the object to be read from the buffer
342-
/// - count: count of bytes in memory
343-
@discardableResult
344-
@inline(__always)
345-
public func withUnsafePointerToSlice<T>(
346-
index: Int,
347-
count: Int,
348-
body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T
349-
{
350-
assert(
351-
index + count <= _storage.capacity,
352-
"Reading out of bounds is illegal")
353-
return try _storage.readWithUnsafeRawPointer(position: index) {
354-
try body(UnsafeRawBufferPointer(start: $0, count: count))
355-
}
356-
}
357-
358-
#if !os(WASI)
359-
/// Reads a string from the buffer and encodes it to a swift string
360-
/// - Parameters:
361-
/// - index: index of the string in the buffer
362-
/// - count: length of the string
363-
/// - type: Encoding of the string
364401
@inline(__always)
365402
public func readString(
366403
at index: Int,
367404
count: Int,
368-
type: String.Encoding = .utf8) -> String?
405+
type: String.Encoding) -> String?
369406
{
370407
assert(
371408
index + count <= _storage.capacity,
@@ -379,7 +416,7 @@ public struct ByteBuffer {
379416
encoding: type)
380417
}
381418
}
382-
#else
419+
383420
/// Reads a string from the buffer and encodes it to a swift string
384421
/// - Parameters:
385422
/// - index: index of the string in the buffer
@@ -396,31 +433,24 @@ public struct ByteBuffer {
396433
String(cString: $0.bindMemory(to: UInt8.self, capacity: count))
397434
}
398435
}
399-
#endif
400436

401-
/// Creates a new Flatbuffer object that's duplicated from the current one
402-
/// - Parameter removeBytes: the amount of bytes to remove from the current Size
403-
@inline(__always)
404-
public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer {
405-
assert(removeBytes > 0, "Can NOT remove negative bytes")
406-
assert(
407-
removeBytes < _storage.capacity,
408-
"Can NOT remove more bytes than the ones allocated")
409-
return ByteBuffer(
410-
blob: _storage.retainedBlob,
411-
count: _storage.capacity,
412-
removing: _readerIndex &- removeBytes)
413-
}
414-
415-
/// SkipPrefix Skips the first 4 bytes in case one of the following
416-
/// functions are called `getPrefixedSizeCheckedRoot` & `getPrefixedSizeRoot`
417-
/// which allows us to skip the first 4 bytes instead of recreating the buffer
437+
/// Provides a pointer towards the underlying primitive types
438+
/// - Parameters:
439+
/// - index: index of the object to be read from the buffer
440+
/// - count: count of bytes in memory
418441
@discardableResult
419-
@usableFromInline
420442
@inline(__always)
421-
mutating func skipPrefix() -> Int32 {
422-
_readerIndex = _readerIndex &- MemoryLayout<Int32>.size
423-
return read(def: Int32.self, position: 0)
443+
public func withUnsafePointerToSlice<T>(
444+
index: Int,
445+
count: Int,
446+
body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T
447+
{
448+
assert(
449+
index + count <= _storage.capacity,
450+
"Reading out of bounds is illegal")
451+
return try _storage.readWithUnsafeRawPointer(position: index) {
452+
try body(UnsafeRawBufferPointer(start: $0, count: count))
453+
}
424454
}
425455

426456
@discardableResult
@@ -450,15 +480,3 @@ public struct ByteBuffer {
450480
try _storage.readWithUnsafeRawPointer(position: position, body)
451481
}
452482
}
453-
454-
extension ByteBuffer: CustomDebugStringConvertible {
455-
456-
public var debugDescription: String {
457-
"""
458-
buffer located at: \(_storage.retainedBlob),
459-
with capacity of \(_storage.capacity),
460-
{ writtenSize: \(_readerIndex), readerSize: \(reader),
461-
size: \(size) }
462-
"""
463-
}
464-
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2024 Google Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import Foundation
18+
19+
public struct FixedTypedVector: FlexBufferVector {
20+
public let byteBuffer: ByteBuffer
21+
public let offset: Int
22+
public let type: FlexBufferType
23+
public let count: Int
24+
public var isEmpty: Bool { count == 0 }
25+
26+
let byteWidth: UInt8
27+
28+
@inline(__always)
29+
init(
30+
byteBuffer: ByteBuffer,
31+
offset: Int,
32+
byteWidth: UInt8,
33+
type: FlexBufferType,
34+
count: Int)
35+
{
36+
self.byteBuffer = byteBuffer
37+
self.offset = offset
38+
self.byteWidth = byteWidth
39+
self.type = type
40+
self.count = count
41+
}
42+
43+
@inline(__always)
44+
public subscript(index: Int) -> Reference? {
45+
let elementOffset = offset &+ (numericCast(index) &* numericCast(byteWidth))
46+
return Reference(
47+
byteBuffer: byteBuffer,
48+
offset: elementOffset,
49+
parentWidth: byteWidth,
50+
byteWidth: 1,
51+
type: type)
52+
}
53+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2024 Google Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import Foundation
18+
19+
protocol FlexBufferVector: Sized & FlexBufferContiguousBytes {
20+
subscript(index: Int) -> Reference? { get }
21+
}
22+
23+
extension FlexBufferVector {
24+
public func jsonBuilder(json: inout String) {
25+
json += "["
26+
for i in 0..<count {
27+
if let val = self[i]?.jsonString() {
28+
let comma = i == count - 1 ? "" : ", "
29+
json += "\(val)\(comma)"
30+
}
31+
}
32+
json += "]"
33+
}
34+
}
35+
36+
public protocol FlexBufferContiguousBytes {
37+
var byteBuffer: ByteBuffer { get }
38+
var offset: Int { get }
39+
var count: Int { get }
40+
41+
func withUnsafeRawBufferPointer<Result>(
42+
_ body: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result
43+
}
44+
45+
extension FlexBufferContiguousBytes {
46+
public func withUnsafeRawBufferPointer<Result>(
47+
_ body: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result
48+
{
49+
try byteBuffer.withUnsafePointerToSlice(
50+
index: offset,
51+
count: count,
52+
body: body)
53+
}
54+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2024 Google Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
public struct Map: Sized {
18+
let byteBuffer: ByteBuffer
19+
let offset: Int
20+
let byteWidth: UInt8
21+
public let keys: TypedVector
22+
public let count: Int
23+
24+
public var values: Vector {
25+
return Vector(byteBuffer: byteBuffer, offset: offset, byteWidth: byteWidth)
26+
}
27+
28+
@inline(__always)
29+
init(byteBuffer: ByteBuffer, offset: Int, byteWidth: UInt8) {
30+
self.byteBuffer = byteBuffer
31+
self.offset = offset
32+
self.byteWidth = byteWidth
33+
34+
count = getCount(buffer: byteBuffer, offset: offset, byteWidth: byteWidth)
35+
keys = TypedVector.mapKeys(
36+
byteBuffer: byteBuffer,
37+
offset: offset,
38+
byteWidth: byteWidth)
39+
}
40+
41+
@inline(__always)
42+
public subscript(key: String) -> Reference? {
43+
let count = keys.count
44+
let mid = count / 2
45+
guard let position = binarySearch(vector: keys, target: key)
46+
else { return nil }
47+
48+
return getReference(at: position)
49+
}
50+
}
51+
52+
extension Map {
53+
public func jsonBuilder(json: inout String) {
54+
json += "{"
55+
for i in 0..<count {
56+
if let key = keys[i]?.cString {
57+
let comma = i == count - 1 ? "" : ", "
58+
let value = values[i]?.jsonString() ?? StaticJSON.null
59+
json += "\"\(key)\": \(value)\(comma)"
60+
}
61+
}
62+
json += "}"
63+
}
64+
}

0 commit comments

Comments
 (0)