• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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