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 /// `Table` is a Flatbuffers object that can read, 20 /// mutate scalar fields within a valid flatbuffers buffer 21 @frozen 22 public struct Table { 23 24 /// Hosting Bytebuffer 25 public private(set) var bb: ByteBuffer 26 /// Current position of the table within the buffer 27 public private(set) var position: Int32 28 29 /// Initializer for the table interface to allow generated code to read 30 /// data from memory 31 /// - Parameters: 32 /// - bb: ByteBuffer that stores data 33 /// - position: Current table position 34 /// - Note: This will `CRASH` if read on a big endian machine 35 public init(bb: ByteBuffer, position: Int32 = 0) { 36 guard isLitteEndian else { 37 fatalError( 38 "Reading/Writing a buffer in big endian machine is not supported on swift") 39 } 40 self.bb = bb 41 self.position = position 42 } 43 44 /// Gets the offset of the current field within the buffer by reading 45 /// the vtable 46 /// - Parameter o: current offset 47 /// - Returns: offset of field within buffer offsetnull48 public func offset(_ o: Int32) -> Int32 { 49 let vtable = position - bb.read(def: Int32.self, position: Int(position)) 50 return o < bb 51 .read(def: VOffset.self, position: Int(vtable)) ? Int32(bb.read( 52 def: Int16.self, 53 position: Int(vtable + o))) : 0 54 } 55 56 /// Gets the indirect offset of the current stored object 57 /// (applicable only for object arrays) 58 /// - Parameter o: current offset 59 /// - Returns: offset of field within buffer indirectnull60 public func indirect(_ o: Int32) -> Int32 { 61 o + bb.read(def: Int32.self, position: Int(o)) 62 } 63 64 /// String reads from the buffer with respect to position of the current table. 65 /// - Parameter offset: Offset of the string stringnull66 public func string(at offset: Int32) -> String? { 67 directString(at: offset + position) 68 } 69 70 /// Direct string reads from the buffer disregarding the position of the table. 71 /// It would be preferable to use string unless the current position of the table 72 /// is not needed 73 /// - Parameter offset: Offset of the string directStringnull74 public func directString(at offset: Int32) -> String? { 75 var offset = offset 76 offset += bb.read(def: Int32.self, position: Int(offset)) 77 let count = bb.read(def: Int32.self, position: Int(offset)) 78 let position = Int(offset) + MemoryLayout<Int32>.size 79 return bb.readString(at: position, count: Int(count)) 80 } 81 82 /// Reads from the buffer with respect to the position in the table. 83 /// - Parameters: 84 /// - type: Type of Element that needs to be read from the buffer 85 /// - o: Offset of the Element readBuffer<T>null86 public func readBuffer<T>(of type: T.Type, at o: Int32) -> T { 87 directRead(of: T.self, offset: o + position) 88 } 89 90 /// Reads from the buffer disregarding the position of the table. 91 /// It would be used when reading from an 92 /// ``` 93 /// let offset = __t.offset(10) 94 /// //Only used when the we already know what is the 95 /// // position in the table since __t.vector(at:) 96 /// // returns the index with respect to the position 97 /// __t.directRead(of: Byte.self, 98 /// offset: __t.vector(at: offset) + index * 1) 99 /// ``` 100 /// - Parameters: 101 /// - type: Type of Element that needs to be read from the buffer 102 /// - o: Offset of the Element directRead<T>null103 public func directRead<T>(of type: T.Type, offset o: Int32) -> T { 104 let r = bb.read(def: T.self, position: Int(o)) 105 return r 106 } 107 108 /// Returns that current `Union` object at a specific offset 109 /// by adding offset to the current position of table 110 /// - Parameter o: offset 111 /// - Returns: A flatbuffers object union<T: FlatbuffersInitializable>null112 public func union<T: FlatbuffersInitializable>(_ o: Int32) -> T { 113 let o = o + position 114 return directUnion(o) 115 } 116 117 /// Returns a direct `Union` object at a specific offset 118 /// - Parameter o: offset 119 /// - Returns: A flatbuffers object directUnion<T: FlatbuffersInitializable>null120 public func directUnion<T: FlatbuffersInitializable>(_ o: Int32) -> T { 121 T.init(bb, o: o + bb.read(def: Int32.self, position: Int(o))) 122 } 123 124 /// Returns a vector of type T at a specific offset 125 /// This should only be used by `Scalars` 126 /// - Parameter off: Readable offset 127 /// - Returns: Returns a vector of type [T] getVector<T>null128 public func getVector<T>(at off: Int32) -> [T]? { 129 let o = offset(off) 130 guard o != 0 else { return nil } 131 return bb.readSlice(index: Int(vector(at: o)), count: Int(vector(count: o))) 132 } 133 134 /// Vector count gets the count of Elements within the array 135 /// - Parameter o: start offset of the vector 136 /// - returns: Count of elements vectornull137 public func vector(count o: Int32) -> Int32 { 138 var o = o 139 o += position 140 o += bb.read(def: Int32.self, position: Int(o)) 141 return bb.read(def: Int32.self, position: Int(o)) 142 } 143 144 /// Vector start index in the buffer 145 /// - Parameter o:start offset of the vector 146 /// - returns: the start index of the vector vectornull147 public func vector(at o: Int32) -> Int32 { 148 var o = o 149 o += position 150 return o + bb.read(def: Int32.self, position: Int(o)) + 4 151 } 152 153 /// Reading an indirect offset of a table. 154 /// - Parameters: 155 /// - o: position within the buffer 156 /// - fbb: ByteBuffer 157 /// - Returns: table offset indirectnull158 static public func indirect(_ o: Int32, _ fbb: ByteBuffer) -> Int32 { 159 o + fbb.read(def: Int32.self, position: Int(o)) 160 } 161 162 /// Gets a vtable value according to an table Offset and a field offset 163 /// - Parameters: 164 /// - o: offset relative to entire buffer 165 /// - vOffset: Field offset within a vtable 166 /// - fbb: ByteBuffer 167 /// - Returns: an position of a field 168 static public func offset( 169 _ o: Int32, 170 vOffset: Int32, 171 fbb: ByteBuffer) -> Int32 172 { 173 let vTable = Int32(fbb.capacity) - o 174 return vTable + Int32(fbb.read( 175 def: Int16.self, 176 position: Int(vTable + vOffset - fbb.read( 177 def: Int32.self, 178 position: Int(vTable))))) 179 } 180 181 /// Compares two objects at offset A and offset B within a ByteBuffer 182 /// - Parameters: 183 /// - off1: first offset to compare 184 /// - off2: second offset to compare 185 /// - fbb: Bytebuffer 186 /// - Returns: returns the difference between 187 static public func compare( 188 _ off1: Int32, 189 _ off2: Int32, 190 fbb: ByteBuffer) -> Int32 191 { 192 let memorySize = Int32(MemoryLayout<Int32>.size) 193 let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1)) 194 let _off2 = off2 + fbb.read(def: Int32.self, position: Int(off2)) 195 let len1 = fbb.read(def: Int32.self, position: Int(_off1)) 196 let len2 = fbb.read(def: Int32.self, position: Int(_off2)) 197 let startPos1 = _off1 + memorySize 198 let startPos2 = _off2 + memorySize 199 let minValue = min(len1, len2) 200 for i in 0...minValue { 201 let b1 = fbb.read(def: Int8.self, position: Int(i + startPos1)) 202 let b2 = fbb.read(def: Int8.self, position: Int(i + startPos2)) 203 if b1 != b2 { 204 return Int32(b2 - b1) 205 } 206 } 207 return len1 - len2 208 } 209 210 /// Compares two objects at offset A and array of `Bytes` within a ByteBuffer 211 /// - Parameters: 212 /// - off1: Offset to compare to 213 /// - key: bytes array to compare to 214 /// - fbb: Bytebuffer 215 /// - Returns: returns the difference between 216 static public func compare( 217 _ off1: Int32, 218 _ key: [Byte], 219 fbb: ByteBuffer) -> Int32 220 { 221 let memorySize = Int32(MemoryLayout<Int32>.size) 222 let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1)) 223 let len1 = fbb.read(def: Int32.self, position: Int(_off1)) 224 let len2 = Int32(key.count) 225 let startPos1 = _off1 + memorySize 226 let minValue = min(len1, len2) 227 for i in 0..<minValue { 228 let b = fbb.read(def: Int8.self, position: Int(i + startPos1)) 229 let byte = key[Int(i)] 230 if b != byte { 231 return Int32(b - Int8(byte)) 232 } 233 } 234 return len1 - len2 235 } 236 } 237