1 /*
2 * Copyright 2021 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 @file:Suppress("NOTHING_TO_INLINE")
17
18 package com.google.flatbuffers.kotlin
19
20 import kotlin.jvm.JvmInline
21
22 @JvmInline
23 public value class BitWidth(public val value: Int) {
maxnull24 public inline fun max(other: BitWidth): BitWidth = if (this.value >= other.value) this else other
25 }
26
27 @JvmInline
28 public value class ByteWidth(public val value: Int)
29
30 @JvmInline
31 public value class FlexBufferType(public val value: Int) {
32 public operator fun minus(other: FlexBufferType): FlexBufferType = FlexBufferType(this.value - other.value)
33 public operator fun plus(other: FlexBufferType): FlexBufferType = FlexBufferType(this.value + other.value)
34 public operator fun compareTo(other: FlexBufferType): Int = this.value - other.value
35 }
36
timesnull37 internal operator fun Int.times(width: ByteWidth): Int = this * width.value
38 internal operator fun Int.minus(width: ByteWidth): Int = this - width.value
39 internal operator fun Int.plus(width: ByteWidth): Int = this + width.value
40 internal operator fun Int.minus(type: FlexBufferType): Int = this - type.value
41
42 // Returns a Key string from the buffer starting at index [start]. Key Strings are stored as
43 // C-Strings, ending with '\0'. If zero byte not found returns empty string.
44 internal inline fun ReadBuffer.getKeyString(start: Int): String {
45 val i = findFirst(0.toByte(), start)
46 return if (i >= 0) getString(start, i - start) else ""
47 }
48
49 // read unsigned int with size byteWidth and return as a 64-bit integer
readULongnull50 internal inline fun ReadBuffer.readULong(end: Int, byteWidth: ByteWidth): ULong {
51 return when (byteWidth.value) {
52 1 -> this.getUByte(end).toULong()
53 2 -> this.getUShort(end).toULong()
54 4 -> this.getUInt(end).toULong()
55 8 -> this.getULong(end)
56 else -> error("invalid byte width $byteWidth for scalar unsigned integer")
57 }
58 }
59
readFloatnull60 internal inline fun ReadBuffer.readFloat(end: Int, byteWidth: ByteWidth): Double {
61 return when (byteWidth.value) {
62 4 -> this.getFloat(end).toDouble()
63 8 -> this.getDouble(end)
64 else -> error("invalid byte width $byteWidth for floating point scalar") // we should never reach here
65 }
66 }
67 // return position on the [ReadBuffer] of the element that the offset is pointing to
68 // we assume all offset fits on a int, since ReadBuffer operates with that assumption
indirectnull69 internal inline fun ReadBuffer.indirect(offset: Int, byteWidth: ByteWidth): Int = offset - readInt(offset, byteWidth)
70 // returns the size of an array-like element from [ReadBuffer].
71 internal inline fun ReadBuffer.readSize(end: Int, byteWidth: ByteWidth) = readInt(end - byteWidth, byteWidth)
72 internal inline fun ReadBuffer.readUInt(end: Int, byteWidth: ByteWidth): UInt = readULong(end, byteWidth).toUInt()
73 internal inline fun ReadBuffer.readInt(end: Int, byteWidth: ByteWidth): Int = readULong(end, byteWidth).toInt()
74 internal inline fun ReadBuffer.readLong(end: Int, byteWidth: ByteWidth): Long = readULong(end, byteWidth).toLong()
75
76 internal fun IntArray.widthInUBits(): BitWidth = arrayWidthInUBits(this.size) { this[it].toULong().widthInUBits() }
<lambda>null77 internal fun ShortArray.widthInUBits(): BitWidth = arrayWidthInUBits(this.size) { this[it].toULong().widthInUBits() }
<lambda>null78 internal fun LongArray.widthInUBits(): BitWidth = arrayWidthInUBits(this.size) { this[it].toULong().widthInUBits() }
79
arrayWidthInUBitsnull80 private inline fun arrayWidthInUBits(size: Int, crossinline elemWidthBlock: (Int) -> BitWidth): BitWidth {
81 // Figure out smallest bit width we can store this vector with.
82 var bitWidth = W_8.max(size.toULong().widthInUBits())
83 // Check bit widths and types for all elements.
84 for (i in 0 until size) {
85 // since we know its inline types we can just assume elmentWidth to be the value width in bits.
86 bitWidth = bitWidth.max(elemWidthBlock(i))
87 }
88 return bitWidth
89 }
90
widthInUBitsnull91 internal fun ULong.widthInUBits(): BitWidth = when {
92 this <= MAX_UBYTE_ULONG -> W_8
93 this <= UShort.MAX_VALUE -> W_16
94 this <= UInt.MAX_VALUE -> W_32
95 else -> W_64
96 }
97
98 // returns the number of bytes needed for padding the scalar of size scalarSize.
paddingBytesnull99 internal inline fun paddingBytes(bufSize: Int, scalarSize: Int): Int = bufSize.inv() + 1 and scalarSize - 1
100
101 internal inline fun FlexBufferType.isInline(): Boolean = this.value <= T_FLOAT.value || this == T_BOOL
102
103 internal fun FlexBufferType.isScalar(): Boolean = when (this) {
104 T_INT, T_UINT, T_FLOAT, T_BOOL -> true
105 else -> false
106 }
107
isIndirectScalarnull108 internal fun FlexBufferType.isIndirectScalar(): Boolean = when (this) {
109 T_INDIRECT_INT, T_INDIRECT_UINT, T_INDIRECT_FLOAT -> true
110 else -> false
111 }
112
isTypedVectornull113 internal fun FlexBufferType.isTypedVector(): Boolean =
114 this >= T_VECTOR_INT && this <= T_VECTOR_STRING_DEPRECATED || this == T_VECTOR_BOOL
115
116 internal fun FlexBufferType.isTypedVectorElementType(): Boolean =
117 (this.value in T_INT.value..T_KEY.value) || this == T_BOOL
118
119 // returns the typed vector of a given scalar type.
120 internal fun FlexBufferType.toTypedVector(): FlexBufferType = (this - T_INT) + T_VECTOR_INT
121 // returns the element type of given typed vector.
122 internal fun FlexBufferType.toElementTypedVector(): FlexBufferType = this - T_VECTOR_INT + T_INT
123
124 // Holds information about the elements inserted on the buffer.
125 internal data class Value(
126 var type: FlexBufferType = T_INT,
127 var key: Int = -1,
128 var minBitWidth: BitWidth = W_8,
129 var iValue: ULong = 0UL, // integer value
130 var dValue: Double = 0.0 // TODO(paulovap): maybe we can keep floating type on iValue as well.
131 ) { // float value
132
133 inline fun storedPackedType(parentBitWidth: BitWidth = W_8): Byte = packedType(storedWidth(parentBitWidth), type)
134
135 private inline fun packedType(bitWidth: BitWidth, type: FlexBufferType): Byte =
136 (bitWidth.value or (type.value shl 2)).toByte()
137
138 private inline fun storedWidth(parentBitWidth: BitWidth): BitWidth =
139 if (type.isInline()) minBitWidth.max(parentBitWidth) else minBitWidth
140
141 fun elemWidth(bufSize: Int, elemIndex: Int): BitWidth =
142 elemWidth(type, minBitWidth, iValue.toLong(), bufSize, elemIndex)
143 }
144
elemWidthnull145 internal fun elemWidth(
146 type: FlexBufferType,
147 minBitWidth: BitWidth,
148 iValue: Long,
149 bufSize: Int,
150 elemIndex: Int
151 ): BitWidth {
152 if (type.isInline()) return minBitWidth
153
154 // We have an absolute offset, but want to store a relative offset
155 // elem_index elements beyond the current buffer end. Since whether
156 // the relative offset fits in a certain byte_width depends on
157 // the size of the elements before it (and their alignment), we have
158 // to test for each size in turn.
159 // Original implementation checks for largest scalar
160 // which is long unsigned int
161 var byteWidth = 1
162 while (byteWidth <= 32) {
163 // Where are we going to write this offset?
164 val offsetLoc: Int = bufSize + paddingBytes(bufSize, byteWidth) + elemIndex * byteWidth
165 // Compute relative offset.
166 val offset: Int = offsetLoc - iValue.toInt()
167 // Does it fit?
168 val bitWidth = offset.toULong().widthInUBits()
169 if (1 shl bitWidth.value == byteWidth) return bitWidth
170 byteWidth *= 2
171 }
172 return W_64
173 }
174
175 // For debugging purposes, convert type to a human-readable string.
typeToStringnull176 internal fun FlexBufferType.typeToString(): String = when (this) {
177 T_NULL -> "Null"
178 T_INT -> "Int"
179 T_UINT -> "UInt"
180 T_FLOAT -> "Float"
181 T_KEY -> "Key"
182 T_STRING -> "String"
183 T_INDIRECT_INT -> "IndirectInt"
184 T_INDIRECT_UINT -> "IndirectUInt"
185 T_INDIRECT_FLOAT -> "IndirectFloat"
186 T_MAP -> "Map"
187 T_VECTOR -> "Vector"
188 T_VECTOR_INT -> "IntVector"
189 T_VECTOR_UINT -> "UIntVector"
190 T_VECTOR_FLOAT -> "FloatVector"
191 T_VECTOR_KEY -> "KeyVector"
192 T_VECTOR_STRING_DEPRECATED -> "StringVectorDeprecated"
193 T_VECTOR_INT2 -> "Int2Vector"
194 T_VECTOR_UINT2 -> "UInt2Vector"
195 T_VECTOR_FLOAT2 -> "Float2Vector"
196 T_VECTOR_INT3 -> "Int3Vector"
197 T_VECTOR_UINT3 -> "UInt3Vector"
198 T_VECTOR_FLOAT3 -> "Float3Vector"
199 T_VECTOR_INT4 -> "Int4Vector"
200 T_VECTOR_UINT4 -> "UInt4Vector"
201 T_VECTOR_FLOAT4 -> "Float4Vector"
202 T_BLOB -> "BlobVector"
203 T_BOOL -> "BoolVector"
204 T_VECTOR_BOOL -> "BoolVector"
205 else -> "UnknownType"
206 }
207
208 // Few repeated values used in hot path is cached here
emptyBlobnull209 internal fun emptyBlob() = Blob(emptyBuffer, 1, ByteWidth(1))
210 internal fun emptyVector() = Vector(emptyBuffer, 1, ByteWidth(1))
211 internal fun emptyMap() = Map(ArrayReadWriteBuffer(3), 3, ByteWidth(1))
212 internal fun nullReference() = Reference(emptyBuffer, 1, ByteWidth(0), T_NULL.value)
213 internal fun nullKey() = Key(emptyBuffer, 1)
214
215 internal const val ZeroByte = 0.toByte()
216 internal const val MAX_UBYTE_ULONG = 255UL
217 internal const val MAX_UBYTE = 255
218 internal const val MAX_USHORT = 65535
219
220 // value bit width possible sizes
221 internal val W_8 = BitWidth(0)
222 internal val W_16 = BitWidth(1)
223 internal val W_32 = BitWidth(2)
224 internal val W_64 = BitWidth(3)
225
226 // These are used as the upper 6 bits of a type field to indicate the actual type.
227 internal val T_INVALID = FlexBufferType(-1)
228 internal val T_NULL = FlexBufferType(0)
229 internal val T_INT = FlexBufferType(1)
230 internal val T_UINT = FlexBufferType(2)
231 internal val T_FLOAT = FlexBufferType(3) // Types above stored inline, types below are stored in an offset.
232 internal val T_KEY = FlexBufferType(4)
233 internal val T_STRING = FlexBufferType(5)
234 internal val T_INDIRECT_INT = FlexBufferType(6)
235 internal val T_INDIRECT_UINT = FlexBufferType(7)
236 internal val T_INDIRECT_FLOAT = FlexBufferType(8)
237 internal val T_MAP = FlexBufferType(9)
238 internal val T_VECTOR = FlexBufferType(10) // Untyped.
239 internal val T_VECTOR_INT = FlexBufferType(11) // Typed any size = stores no type table).
240 internal val T_VECTOR_UINT = FlexBufferType(12)
241 internal val T_VECTOR_FLOAT = FlexBufferType(13)
242 internal val T_VECTOR_KEY = FlexBufferType(14)
243 // DEPRECATED, use FBT_VECTOR or FBT_VECTOR_KEY instead.
244 // more info on https://github.com/google/flatbuffers/issues/5627.
245 internal val T_VECTOR_STRING_DEPRECATED = FlexBufferType(15)
246 internal val T_VECTOR_INT2 = FlexBufferType(16) // Typed tuple = no type table; no size field).
247 internal val T_VECTOR_UINT2 = FlexBufferType(17)
248 internal val T_VECTOR_FLOAT2 = FlexBufferType(18)
249 internal val T_VECTOR_INT3 = FlexBufferType(19) // Typed triple = no type table; no size field).
250 internal val T_VECTOR_UINT3 = FlexBufferType(20)
251 internal val T_VECTOR_FLOAT3 = FlexBufferType(21)
252 internal val T_VECTOR_INT4 = FlexBufferType(22) // Typed quad = no type table; no size field).
253 internal val T_VECTOR_UINT4 = FlexBufferType(23)
254 internal val T_VECTOR_FLOAT4 = FlexBufferType(24)
255 internal val T_BLOB = FlexBufferType(25)
256 internal val T_BOOL = FlexBufferType(26)
257 internal val T_VECTOR_BOOL = FlexBufferType(36) // To Allow the same type of conversion of type to vector type
258