Squashed 'third_party/flatbuffers/' content from commit 595bf0007
git-subtree-dir: third_party/flatbuffers git-subtree-split: 595bf0007ab1929570c7671f091313c8fc20644e
This commit is contained in:
+616
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Represent a chunk of data, where FlexBuffers will be read from.
|
||||
*/
|
||||
public interface ReadBuffer {
|
||||
|
||||
/**
|
||||
* Scan through the buffer for first byte matching value.
|
||||
* @param value to be match
|
||||
* @param start inclusive initial position to start searching
|
||||
* @param end exclusive final position of the search
|
||||
* @return position of a match or -1
|
||||
*/
|
||||
public fun findFirst(value: Byte, start: Int, end: Int = limit): Int
|
||||
|
||||
/**
|
||||
* Read boolean from the buffer. Booleans as stored as a single byte
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return [Boolean] element
|
||||
*/
|
||||
public fun getBoolean(index: Int): Boolean
|
||||
|
||||
/**
|
||||
* Read a [Byte] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a byte
|
||||
*/
|
||||
public operator fun get(index: Int): Byte
|
||||
|
||||
/**
|
||||
* Read a [UByte] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a [UByte]
|
||||
*/
|
||||
public fun getUByte(index: Int): UByte
|
||||
|
||||
/**
|
||||
* Read a [Short] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a [Short]
|
||||
*/
|
||||
public fun getShort(index: Int): Short
|
||||
|
||||
/**
|
||||
* Read a [UShort] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a [UShort]
|
||||
*/
|
||||
public fun getUShort(index: Int): UShort
|
||||
|
||||
/**
|
||||
* Read a [Int] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return an [Int]
|
||||
*/
|
||||
public fun getInt(index: Int): Int
|
||||
|
||||
/**
|
||||
* Read a [UInt] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return an [UInt]
|
||||
*/
|
||||
public fun getUInt(index: Int): UInt
|
||||
|
||||
/**
|
||||
* Read a [Long] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a [Long]
|
||||
*/
|
||||
public fun getLong(index: Int): Long
|
||||
|
||||
/**
|
||||
* Read a [ULong] from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a [ULong]
|
||||
*/
|
||||
public fun getULong(index: Int): ULong
|
||||
|
||||
/**
|
||||
* Read a 32-bit float from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a float
|
||||
*/
|
||||
public fun getFloat(index: Int): Float
|
||||
|
||||
/**
|
||||
* Read a 64-bit float from the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
* @return a double
|
||||
*/
|
||||
public fun getDouble(index: Int): Double
|
||||
|
||||
/**
|
||||
* Read a UTF-8 string from the buffer.
|
||||
* @param start initial element of the string
|
||||
* @param size size of the string in bytes.
|
||||
* @return a `String`
|
||||
*/
|
||||
public fun getString(start: Int = 0, size: Int = limit): String
|
||||
|
||||
/**
|
||||
* Read a ByteArray from the buffer.
|
||||
* @param start position from the [ReadBuffer] to be read
|
||||
* @param length maximum number of bytes to be written in the buffer
|
||||
*/
|
||||
public fun getBytes(array: ByteArray, start: Int, length: Int = array.size)
|
||||
|
||||
/**
|
||||
* Expose [ReadBuffer] as an array of bytes.
|
||||
* This method is meant to be as efficient as possible, so for an array-backed [ReadBuffer], it should
|
||||
* return its own internal data. In case access to internal data is not possible,
|
||||
* a copy of the data into an array of bytes might occur.
|
||||
* @return [ReadBuffer] as an array of bytes
|
||||
*/
|
||||
public fun data(): ByteArray
|
||||
|
||||
/**
|
||||
* Creates a new [ReadBuffer] point to a region of the current buffer, starting at [start] with size [size].
|
||||
* @param start starting position of the [ReadBuffer]
|
||||
* @param size in bytes of the [ReadBuffer]
|
||||
* @return [ReadBuffer] slice.
|
||||
*/
|
||||
public fun slice(start: Int, size: Int): ReadBuffer
|
||||
|
||||
/**
|
||||
* Defines the size of the message in the buffer. It also determines last position that buffer
|
||||
* can be read. Last byte to be accessed is in position `limit() -1`.
|
||||
* @return indicate last position
|
||||
*/
|
||||
public val limit: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to represent a read-write buffers. This interface will be used to access and write FlexBuffer messages.
|
||||
*/
|
||||
public interface ReadWriteBuffer : ReadBuffer {
|
||||
/**
|
||||
* Clears (resets) the buffer so that it can be reused. Write position will be set to the start.
|
||||
*/
|
||||
public fun clear()
|
||||
|
||||
/**
|
||||
* Request capacity of the buffer relative to [writePosition]. In case buffer is already larger
|
||||
* than the requested, this method will just return true. Otherwise,
|
||||
* It might try to resize the buffer. In case of being unable to allocate
|
||||
* enough memory, an exception will be thrown.
|
||||
* @param additional capacity in bytes to be added on top of [writePosition]
|
||||
* @param copyAtEnd copy current data at the end of new underlying buffer
|
||||
* @return new capacity in bytes
|
||||
*/
|
||||
public fun requestAdditionalCapacity(additional: Int, copyAtEnd: Boolean = false): Int =
|
||||
requestCapacity(writePosition + additional, copyAtEnd)
|
||||
|
||||
/**
|
||||
* Request capacity of the buffer in absolute values. In case buffer is already larger
|
||||
* than the requested the method is a no-op. Otherwise,
|
||||
* It might try to resize the buffer. In case of being unable to allocate
|
||||
* enough memory, an exception will be thrown.
|
||||
* @param capacity new capacity
|
||||
* @param copyAtEnd copy current data at the end of new underlying buffer
|
||||
* @return new capacity in bytes
|
||||
*/
|
||||
public fun requestCapacity(capacity: Int, copyAtEnd: Boolean = false): Int
|
||||
|
||||
/**
|
||||
* Put a [Boolean] into the buffer at [writePosition] . Booleans as stored as single byte.
|
||||
* Write position will be incremented.
|
||||
* @return [Boolean] element
|
||||
*/
|
||||
public fun put(value: Boolean)
|
||||
|
||||
/**
|
||||
* Put an array of bytes into the buffer at [writePosition]. Write position will be incremented.
|
||||
* @param value the data to be copied
|
||||
* @param start initial position on value to be copied
|
||||
* @param length amount of bytes to be copied
|
||||
*/
|
||||
public fun put(value: ByteArray, start: Int = 0, length: Int = value.size)
|
||||
|
||||
/**
|
||||
* Put an array of bytes into the buffer at [writePosition]. Write position will be incremented.
|
||||
* @param value [ReadBuffer] the data to be copied
|
||||
* @param start initial position on value to be copied
|
||||
* @param length amount of bytes to be copied
|
||||
*/
|
||||
public fun put(value: ReadBuffer, start: Int = 0, length: Int = value.limit - start)
|
||||
|
||||
/**
|
||||
* Write a [Byte] into the buffer at [writePosition]. Write position will be incremented.
|
||||
*/
|
||||
public fun put(value: Byte)
|
||||
|
||||
/**
|
||||
* Write a [UByte] into the buffer at [writePosition]. Write position will be incremented.
|
||||
*/
|
||||
public fun put(value: UByte)
|
||||
|
||||
/**
|
||||
* Write a [Short] into in the buffer at [writePosition]. Write position will be incremented.
|
||||
*/
|
||||
public fun put(value: Short)
|
||||
|
||||
/**
|
||||
* Write a [UShort] into in the buffer at [writePosition]. Write position will be incremented.
|
||||
*/
|
||||
public fun put(value: UShort)
|
||||
|
||||
/**
|
||||
* Write a [Int] in the buffer at [writePosition]. Write position will be incremented.
|
||||
*/
|
||||
public fun put(value: Int)
|
||||
|
||||
/**
|
||||
* Write a [UInt] into in the buffer at [writePosition]. Write position will be incremented.
|
||||
*/
|
||||
public fun put(value: UInt)
|
||||
|
||||
/**
|
||||
* Write a [Long] into in the buffer at [writePosition]. Write position will be
|
||||
* incremented.
|
||||
*/
|
||||
public fun put(value: Long)
|
||||
|
||||
/**
|
||||
* Write a [ULong] into in the buffer at [writePosition]. Write position will be
|
||||
* incremented.
|
||||
*/
|
||||
public fun put(value: ULong)
|
||||
|
||||
/**
|
||||
* Write a 32-bit [Float] into the buffer at [writePosition]. Write position will be
|
||||
* incremented.
|
||||
*/
|
||||
public fun put(value: Float)
|
||||
|
||||
/**
|
||||
* Write a 64-bit [Double] into the buffer at [writePosition]. Write position will be
|
||||
* incremented.
|
||||
*/
|
||||
public fun put(value: Double)
|
||||
|
||||
/**
|
||||
* Write a [String] encoded as UTF-8 into the buffer at [writePosition]. Write position will be incremented.
|
||||
* @return size in bytes of the encoded string
|
||||
*/
|
||||
public fun put(value: CharSequence, encodedLength: Int = -1): Int
|
||||
|
||||
/**
|
||||
* Write an array of bytes into the buffer.
|
||||
* @param dstIndex initial position where [src] will be copied into.
|
||||
* @param src the data to be copied.
|
||||
* @param srcStart initial position on [src] that will be copied.
|
||||
* @param srcLength amount of bytes to be copied
|
||||
*/
|
||||
public fun set(dstIndex: Int, src: ByteArray, srcStart: Int = 0, srcLength: Int = src.size)
|
||||
|
||||
/**
|
||||
* Write an array of bytes into the buffer.
|
||||
* @param dstIndex initial position where [src] will be copied into.
|
||||
* @param src the data to be copied.
|
||||
* @param srcStart initial position on [src] that will be copied.
|
||||
* @param srcLength amount of bytes to be copied
|
||||
*/
|
||||
public operator fun set(dstIndex: Int, src: ReadBuffer, srcStart: Int = 0, srcLength: Int)
|
||||
|
||||
/**
|
||||
* Write [Boolean] into a given position [index] on the buffer. Booleans as stored as single byte.
|
||||
* @param index position of the element in buffer
|
||||
*/
|
||||
public operator fun set(index: Int, value: Boolean)
|
||||
|
||||
/**
|
||||
* Write [Byte] into a given position [index] on the buffer.
|
||||
* @param index position of the element in the buffer
|
||||
*/
|
||||
public operator fun set(index: Int, value: Byte)
|
||||
|
||||
/**
|
||||
* Write [UByte] into a given position [index] on the buffer.
|
||||
* @param index position of the element in the buffer
|
||||
*/
|
||||
public operator fun set(index: Int, value: UByte)
|
||||
|
||||
/**
|
||||
Short
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: Short)
|
||||
|
||||
/**
|
||||
* Write [UShort] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: UShort)
|
||||
|
||||
/**
|
||||
* Write [Int] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: Int)
|
||||
|
||||
/**
|
||||
* Write [UInt] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: UInt)
|
||||
|
||||
/**
|
||||
* Write [Long] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: Long)
|
||||
|
||||
/**
|
||||
* Write [ULong] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: ULong)
|
||||
|
||||
/**
|
||||
* Write [Float] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: Float)
|
||||
|
||||
/**
|
||||
* Write [Double] into a given position [index] on the buffer.
|
||||
* @param index position of the element in [ReadBuffer]
|
||||
*/
|
||||
public fun set(index: Int, value: Double)
|
||||
|
||||
public fun fill(value: Byte, start: Int, end: Int)
|
||||
|
||||
/**
|
||||
* Current position of the buffer to be written. It will be automatically updated on [put] operations.
|
||||
*/
|
||||
public var writePosition: Int
|
||||
|
||||
/**
|
||||
* Creates a new [ReadWriteBuffer] point to a region of the current buffer, starting at [offset] with size [size].
|
||||
* @param offset starting position of the [ReadWriteBuffer]
|
||||
* @param size in bytes of the [ReadWriteBuffer]
|
||||
* @return [ReadWriteBuffer] slice.
|
||||
*/
|
||||
public fun writeSlice(offset: Int, size: Int): ReadWriteBuffer
|
||||
|
||||
/**
|
||||
* Special operation where we increase the backed buffer size to [capacity]
|
||||
* and shift all already written data to the end of the buffer.
|
||||
*
|
||||
* This function is mostly used when creating a Flatbuffer message, as
|
||||
* data is written from the end of the buffer towards index 0.
|
||||
* @param capacity required in bytes
|
||||
* @return new capacity in bytes
|
||||
*/
|
||||
public fun moveWrittenDataToEnd(capacity: Int): Int
|
||||
|
||||
/**
|
||||
* Maximum size in bytes that the backed buffer supports.
|
||||
*/
|
||||
public val capacity: Int
|
||||
|
||||
/**
|
||||
* Defines last relative position of the backed buffer that can be written.
|
||||
* Any addition to the buffer that goes beyond will throw an exception
|
||||
* instead of regrow the buffer (default behavior).
|
||||
*/
|
||||
public val writeLimit: Int
|
||||
}
|
||||
|
||||
public open class ArrayReadBuffer(protected var buffer: ByteArray,
|
||||
// offsets writePosition against backed buffer e.g. offset = 1, writePosition = 1
|
||||
// will write first byte at position 2 of the backed buffer
|
||||
internal val offset: Int = 0,
|
||||
override val limit: Int = buffer.size - offset) : ReadBuffer {
|
||||
|
||||
|
||||
override fun findFirst(value: Byte, start: Int, end: Int): Int {
|
||||
val e = min(end, limit)
|
||||
val s = max(0, this.offset + start)
|
||||
for (i in s until e) if (buffer[i] == value) return i
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun getBoolean(index: Int): Boolean = buffer[offset + index] != 0.toByte()
|
||||
|
||||
override operator fun get(index: Int): Byte = buffer[offset + index]
|
||||
|
||||
override fun getUByte(index: Int): UByte = buffer.getUByte(offset + index)
|
||||
|
||||
override fun getShort(index: Int): Short = buffer.getShort(offset + index)
|
||||
|
||||
override fun getUShort(index: Int): UShort = buffer.getUShort(offset + index)
|
||||
|
||||
override fun getInt(index: Int): Int = buffer.getInt(offset + index)
|
||||
|
||||
override fun getUInt(index: Int): UInt = buffer.getUInt(offset + index)
|
||||
|
||||
override fun getLong(index: Int): Long = buffer.getLong(offset + index)
|
||||
|
||||
override fun getULong(index: Int): ULong = buffer.getULong(offset + index)
|
||||
|
||||
override fun getFloat(index: Int): Float = buffer.getFloat(offset + index)
|
||||
|
||||
override fun getDouble(index: Int): Double = buffer.getDouble(offset + index)
|
||||
|
||||
override fun getString(start: Int, size: Int): String = buffer.decodeToString(this.offset + start,
|
||||
this.offset + start + size)
|
||||
|
||||
override fun getBytes(array: ByteArray, start: Int, length: Int) {
|
||||
val end = min(this.offset + start + length, buffer.size)
|
||||
var j = 0
|
||||
for (i in this.offset + start until end) {
|
||||
array[j++] = buffer[i]
|
||||
}
|
||||
}
|
||||
|
||||
override fun data(): ByteArray = buffer
|
||||
|
||||
override fun slice(start: Int, size: Int): ReadBuffer = ArrayReadBuffer(buffer, this.offset + start, size)
|
||||
}
|
||||
/**
|
||||
* Implements `[ReadWriteBuffer]` using [ByteArray] as backing buffer. Using array of bytes are
|
||||
* usually faster than `ByteBuffer`.
|
||||
*
|
||||
* This class is not thread-safe, meaning that
|
||||
* it must operate on a single thread. Operating from
|
||||
* multiple thread leads into an undefined behavior
|
||||
*
|
||||
* All operations assume Little Endian byte order.
|
||||
*/
|
||||
|
||||
public class ArrayReadWriteBuffer(
|
||||
buffer: ByteArray,
|
||||
offset: Int = 0,
|
||||
// Defines last position of the backed buffer that can be written.
|
||||
// Any addition to the buffer that goes beyond will throw an exception
|
||||
// instead of regrow the buffer (default behavior).
|
||||
public override val writeLimit: Int = -1,
|
||||
override var writePosition: Int = offset
|
||||
) : ArrayReadBuffer(buffer, offset, writePosition), ReadWriteBuffer {
|
||||
|
||||
public constructor(initialCapacity: Int = 10) : this(ByteArray(initialCapacity))
|
||||
|
||||
override val limit: Int get() = writePosition
|
||||
|
||||
override fun clear(): Unit = run { writePosition = 0 }
|
||||
|
||||
override fun put(value: Boolean) {
|
||||
set(writePosition, value)
|
||||
writePosition++
|
||||
}
|
||||
|
||||
override fun put(value: ByteArray, start: Int, length: Int) {
|
||||
set(writePosition, value, start, length)
|
||||
writePosition += length
|
||||
}
|
||||
|
||||
override fun put(value: ReadBuffer, start: Int, length: Int) {
|
||||
set(writePosition, value, start, length)
|
||||
writePosition += length
|
||||
}
|
||||
|
||||
override fun put(value: Byte) {
|
||||
set(writePosition, value)
|
||||
writePosition++
|
||||
}
|
||||
|
||||
override fun put(value: UByte) {
|
||||
set(writePosition, value)
|
||||
writePosition++
|
||||
}
|
||||
|
||||
override fun put(value: Short) {
|
||||
set(writePosition, value)
|
||||
writePosition += 2
|
||||
}
|
||||
|
||||
override fun put(value: UShort) {
|
||||
set(writePosition, value)
|
||||
writePosition += 2
|
||||
}
|
||||
|
||||
override fun put(value: Int) {
|
||||
set(writePosition, value)
|
||||
writePosition += 4
|
||||
}
|
||||
|
||||
override fun put(value: UInt) {
|
||||
set(writePosition, value)
|
||||
writePosition += 4
|
||||
}
|
||||
|
||||
override fun put(value: Long) {
|
||||
set(writePosition, value)
|
||||
writePosition += 8
|
||||
}
|
||||
|
||||
override fun put(value: ULong) {
|
||||
set(writePosition, value)
|
||||
writePosition += 8
|
||||
}
|
||||
|
||||
override fun put(value: Float) {
|
||||
set(writePosition, value)
|
||||
writePosition += 4
|
||||
}
|
||||
|
||||
override fun put(value: Double) {
|
||||
set(writePosition, value)
|
||||
writePosition += 8
|
||||
}
|
||||
|
||||
override fun put(value: CharSequence, encodedLength: Int): Int {
|
||||
val length = if (encodedLength != -1) encodedLength else Utf8.encodedLength(value)
|
||||
writePosition = buffer.setCharSequence(writePosition, value)
|
||||
return length
|
||||
}
|
||||
|
||||
override fun set(index: Int, value: Boolean) {
|
||||
buffer[index] = if (value) 1.toByte() else 0.toByte()
|
||||
}
|
||||
|
||||
override fun set(dstIndex: Int, src: ByteArray, srcStart: Int, srcLength: Int) {
|
||||
src.copyInto(buffer, dstIndex, srcStart, srcStart + srcLength)
|
||||
}
|
||||
|
||||
override operator fun set(dstIndex: Int, src: ReadBuffer, srcStart: Int, srcLength: Int) {
|
||||
when(src) {
|
||||
is ArrayReadBuffer -> {
|
||||
src.data().copyInto(buffer, dstIndex, src.offset + srcStart, src.offset + srcStart + srcLength)
|
||||
}
|
||||
else -> {
|
||||
for (i in 0 until srcLength) {
|
||||
buffer[dstIndex + i] = src[srcStart + i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override operator fun set(index: Int, value: Byte) { buffer[index] = value }
|
||||
override operator fun set(index: Int, value: UByte) { buffer.setUByte(index, value) }
|
||||
override operator fun set(index: Int, value: Short) { buffer.setShort(index, value) }
|
||||
override operator fun set(index: Int, value: UShort) { buffer.setUShort(index, value) }
|
||||
override operator fun set(index: Int, value: Int) { buffer.setInt(index, value) }
|
||||
override operator fun set(index: Int, value: UInt) { buffer.setUInt(index, value) }
|
||||
override operator fun set(index: Int, value: Long) { buffer.setLong(index, value) }
|
||||
override operator fun set(index: Int, value: ULong) { buffer.setULong(index, value) }
|
||||
override operator fun set(index: Int, value: Float) { buffer.setFloat(index, value) }
|
||||
override operator fun set(index: Int, value: Double) { buffer.setDouble(index, value) }
|
||||
override fun fill(value: Byte, start: Int, end: Int) { buffer.fill(value, start, end) }
|
||||
|
||||
/**
|
||||
* Request capacity of the buffer. In case buffer is already larger
|
||||
* than the requested, it is a no-op. Otherwise,
|
||||
* It might try to resize the buffer. In case of being unable to allocate
|
||||
* enough memory, an exception will be thrown.
|
||||
* @param capacity new capacity
|
||||
* @param copyAtEnd copy current data at the end of new underlying buffer
|
||||
*/
|
||||
override fun requestCapacity(capacity: Int, copyAtEnd: Boolean): Int {
|
||||
if (capacity < 0) error("Capacity may not be negative (likely a previous int overflow)")
|
||||
|
||||
if (buffer.size >= capacity) return buffer.size
|
||||
|
||||
if (writeLimit > 0 && writeLimit + offset >= buffer.size) error("Buffer in writeLimit mode. In writeLimit mode" +
|
||||
" the buffer does not grow automatically and any write beyond writeLimit will throw exception. " +
|
||||
"(writeLimit: $writeLimit, newCapacity: $capacity")
|
||||
// implemented in the same growing fashion as ArrayList
|
||||
val oldCapacity = buffer.size
|
||||
if (oldCapacity == Int.MAX_VALUE - 8) { // Ensure we don't grow beyond what fits in an int.
|
||||
error("FlatBuffers: cannot grow buffer beyond 2 gigabytes.")
|
||||
}
|
||||
//(old_buf_size & 0xC0000000) != 0 ? MAX_BUFFER_SIZE : old_buf_size << 1;
|
||||
var newCapacity = 8
|
||||
while (newCapacity < capacity) { // Note: this also catches newCapacity int overflow
|
||||
newCapacity = if (newCapacity and -0x40000000 != 0) Int.MAX_VALUE - 8 else newCapacity shl 1
|
||||
}
|
||||
val newBuffer = ByteArray(newCapacity)
|
||||
|
||||
buffer.copyInto(newBuffer, if (copyAtEnd) newBuffer.size - buffer.size else 0)
|
||||
buffer = newBuffer
|
||||
return newCapacity
|
||||
}
|
||||
|
||||
override fun writeSlice(offset: Int, size: Int): ReadWriteBuffer {
|
||||
return ArrayReadWriteBuffer(this.buffer, offset=offset, writeLimit=size)
|
||||
}
|
||||
|
||||
override fun moveWrittenDataToEnd(capacity: Int): Int = requestCapacity(capacity, true)
|
||||
|
||||
override val capacity: Int
|
||||
get() = buffer.size
|
||||
|
||||
}
|
||||
|
||||
public val emptyBuffer: ReadWriteBuffer = ArrayReadWriteBuffer(ByteArray(1))
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.experimental.and
|
||||
|
||||
internal fun ByteArray.getString(index: Int, size: Int): String = Utf8.decodeUtf8Array(this, index, size)
|
||||
|
||||
internal fun ByteArray.setCharSequence(index: Int, value: CharSequence): Int =
|
||||
Utf8.encodeUtf8Array(value, this, index, this.size - index)
|
||||
|
||||
// List of functions that needs to be implemented on all platforms.
|
||||
internal expect inline fun ByteArray.getUByte(index: Int): UByte
|
||||
internal expect inline fun ByteArray.getShort(index: Int): Short
|
||||
internal expect inline fun ByteArray.getUShort(index: Int): UShort
|
||||
internal expect inline fun ByteArray.getInt(index: Int): Int
|
||||
internal expect inline fun ByteArray.getUInt(index: Int): UInt
|
||||
internal expect inline fun ByteArray.getLong(index: Int): Long
|
||||
internal expect inline fun ByteArray.getULong(index: Int): ULong
|
||||
internal expect inline fun ByteArray.getFloat(index: Int): Float
|
||||
internal expect inline fun ByteArray.getDouble(index: Int): Double
|
||||
|
||||
internal expect inline fun ByteArray.setUByte(index: Int, value: UByte)
|
||||
public expect inline fun ByteArray.setShort(index: Int, value: Short)
|
||||
internal expect inline fun ByteArray.setUShort(index: Int, value: UShort)
|
||||
internal expect inline fun ByteArray.setInt(index: Int, value: Int)
|
||||
internal expect inline fun ByteArray.setUInt(index: Int, value: UInt)
|
||||
internal expect inline fun ByteArray.setLong(index: Int, value: Long)
|
||||
internal expect inline fun ByteArray.setULong(index: Int, value: ULong)
|
||||
internal expect inline fun ByteArray.setFloat(index: Int, value: Float)
|
||||
internal expect inline fun ByteArray.setDouble(index: Int, value: Double)
|
||||
|
||||
/**
|
||||
* This implementation uses Little Endian order.
|
||||
*/
|
||||
public object ByteArrayOps {
|
||||
public inline fun getUByte(ary: ByteArray, index: Int): UByte = ary[index].toUByte()
|
||||
public inline fun getShort(ary: ByteArray, index: Int): Short {
|
||||
return (ary[index + 1].toInt() shl 8 or (ary[index].toInt() and 0xff)).toShort()
|
||||
}
|
||||
|
||||
public inline fun getUShort(ary: ByteArray, index: Int): UShort = getShort(ary, index).toUShort()
|
||||
|
||||
public inline fun getInt(ary: ByteArray, index: Int): Int {
|
||||
return (
|
||||
(ary[index + 3].toInt() shl 24) or
|
||||
((ary[index + 2].toInt() and 0xff) shl 16) or
|
||||
((ary[index + 1].toInt() and 0xff) shl 8) or
|
||||
((ary[index].toInt() and 0xff))
|
||||
)
|
||||
}
|
||||
|
||||
public inline fun getUInt(ary: ByteArray, index: Int): UInt = getInt(ary, index).toUInt()
|
||||
|
||||
public inline fun getLong(ary: ByteArray, index: Int): Long {
|
||||
var idx = index
|
||||
return ary[idx++].toLong() and 0xff or
|
||||
(ary[idx++].toLong() and 0xff shl 8) or
|
||||
(ary[idx++].toLong() and 0xff shl 16) or
|
||||
(ary[idx++].toLong() and 0xff shl 24) or
|
||||
(ary[idx++].toLong() and 0xff shl 32) or
|
||||
(ary[idx++].toLong() and 0xff shl 40) or
|
||||
(ary[idx++].toLong() and 0xff shl 48) or
|
||||
(ary[idx].toLong() shl 56)
|
||||
}
|
||||
|
||||
public inline fun getULong(ary: ByteArray, index: Int): ULong = getLong(ary, index).toULong()
|
||||
|
||||
public inline fun setUByte(ary: ByteArray, index: Int, value: UByte) {
|
||||
ary[index] = value.toByte()
|
||||
}
|
||||
public inline fun setShort(ary: ByteArray, index: Int, value: Short) {
|
||||
var idx = index
|
||||
ary[idx++] = (value and 0xff).toByte()
|
||||
ary[idx] = (value.toInt() shr 8 and 0xff).toByte()
|
||||
}
|
||||
|
||||
public inline fun setUShort(ary: ByteArray, index: Int, value: UShort): Unit = setShort(ary, index, value.toShort())
|
||||
|
||||
public inline fun setInt(ary: ByteArray, index: Int, value: Int) {
|
||||
var idx = index
|
||||
ary[idx++] = (value and 0xff).toByte()
|
||||
ary[idx++] = (value shr 8 and 0xff).toByte()
|
||||
ary[idx++] = (value shr 16 and 0xff).toByte()
|
||||
ary[idx] = (value shr 24 and 0xff).toByte()
|
||||
}
|
||||
|
||||
public inline fun setUInt(ary: ByteArray, index: Int, value: UInt): Unit = setInt(ary, index, value.toInt())
|
||||
|
||||
public inline fun setLong(ary: ByteArray, index: Int, value: Long) {
|
||||
var i = value.toInt()
|
||||
setInt(ary, index, i)
|
||||
i = (value shr 32).toInt()
|
||||
setInt(ary, index + 4, i)
|
||||
}
|
||||
|
||||
public inline fun setULong(ary: ByteArray, index: Int, value: ULong): Unit = setLong(ary, index, value.toLong())
|
||||
|
||||
public inline fun setFloat(ary: ByteArray, index: Int, value: Float) {
|
||||
setInt(ary, index, value.toRawBits())
|
||||
}
|
||||
|
||||
public inline fun setDouble(ary: ByteArray, index: Int, value: Double) {
|
||||
setLong(ary, index, value.toRawBits())
|
||||
}
|
||||
|
||||
public inline fun getFloat(ary: ByteArray, index: Int): Float = Float.fromBits(getInt(ary, index))
|
||||
public inline fun getDouble(ary: ByteArray, index: Int): Double = Double.fromBits(getLong(ary, index))
|
||||
}
|
||||
+1105
File diff suppressed because it is too large
Load Diff
+367
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
import kotlin.math.min
|
||||
|
||||
// For now a typealias to guarantee type safety.
|
||||
public typealias UnionOffset = Offset<Any>
|
||||
public typealias UnionOffsetArray = OffsetArray<Any>
|
||||
public typealias StringOffsetArray = OffsetArray<String>
|
||||
|
||||
public inline fun UnionOffsetArray(size: Int, crossinline call: (Int) -> Offset<Any>): UnionOffsetArray =
|
||||
UnionOffsetArray(IntArray(size) { call(it).value })
|
||||
public inline fun StringOffsetArray(size: Int, crossinline call: (Int) -> Offset<String>): StringOffsetArray =
|
||||
StringOffsetArray(IntArray(size) { call(it).value })
|
||||
/**
|
||||
* Represents a "pointer" to a pointer types (table, string, struct) within the buffer
|
||||
*/
|
||||
@JvmInline
|
||||
public value class Offset<T>(public val value: Int) {
|
||||
public fun toUnion(): UnionOffset = UnionOffset(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an array of offsets. Used to avoid boxing
|
||||
* offset types.
|
||||
*/
|
||||
@JvmInline
|
||||
public value class OffsetArray<T>(public val value: IntArray) {
|
||||
public inline val size: Int
|
||||
get() = value.size
|
||||
public inline operator fun get(index: Int): Offset<T> = Offset(value[index])
|
||||
}
|
||||
|
||||
public inline fun <T> OffsetArray(size: Int, crossinline call: (Int) -> Offset<T>): OffsetArray<T> {
|
||||
return OffsetArray(IntArray(size) { call(it).value })
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a "pointer" to a vector type with elements T
|
||||
*/
|
||||
@JvmInline
|
||||
public value class VectorOffset<T>(public val value: Int)
|
||||
|
||||
public fun <T> Int.toOffset(): Offset<T> = Offset(this)
|
||||
|
||||
public operator fun <T> Offset<T>.minus(other: Int): Offset<T> = Offset(this.value - other)
|
||||
|
||||
public operator fun <T> Int.minus(other: Offset<T>): Int {
|
||||
return this - other.value
|
||||
}
|
||||
/**
|
||||
* All tables in the generated code derive from this class, and add their own accessors.
|
||||
*/
|
||||
public open class Table {
|
||||
|
||||
/** Used to hold the position of the `bb` buffer. */
|
||||
public var bufferPos: Int = 0
|
||||
|
||||
/** The underlying ReadWriteBuffer to hold the data of the Table. */
|
||||
public var bb: ReadWriteBuffer = emptyBuffer
|
||||
|
||||
/** Used to hold the vtable position. */
|
||||
public var vtableStart: Int = 0
|
||||
|
||||
/** Used to hold the vtable size. */
|
||||
public var vtableSize: Int = 0
|
||||
|
||||
protected inline fun <reified T> Int.invalid(default: T, crossinline valid: (Int) -> T) : T =
|
||||
if (this != 0) valid(this) else default
|
||||
|
||||
protected inline fun <reified T> lookupField(i: Int, default: T, crossinline found: (Int) -> T) : T =
|
||||
offset(i).invalid(default) { found(it) }
|
||||
|
||||
/**
|
||||
* Look up a field in the vtable.
|
||||
*
|
||||
* @param vtableOffset An `int` offset to the vtable in the Table's ReadWriteBuffer.
|
||||
* @return Returns an offset into the object, or `0` if the field is not present.
|
||||
*/
|
||||
public fun offset(vtableOffset: Int): Int =
|
||||
if (vtableOffset < vtableSize) bb.getShort(vtableStart + vtableOffset).toInt() else 0
|
||||
|
||||
/**
|
||||
* Retrieve a relative offset.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer containing the relative offset.
|
||||
* @return Returns the relative offset stored at `offset`.
|
||||
*/
|
||||
public fun indirect(offset: Int): Int = offset + bb.getInt(offset)
|
||||
|
||||
/**
|
||||
* Create a Java `String` from UTF-8 data stored inside the FlatBuffer.
|
||||
*
|
||||
* This allocates a new string and converts to wide chars upon each access,
|
||||
* which is not very efficient. Instead, each FlatBuffer string also comes with an
|
||||
* accessor based on __vector_as_ReadWriteBuffer below, which is much more efficient,
|
||||
* assuming your Java program can handle UTF-8 data directly.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer.
|
||||
* @return Returns a `String` from the data stored inside the FlatBuffer at `offset`.
|
||||
*/
|
||||
public fun string(offset: Int): String = string(offset, bb)
|
||||
|
||||
/**
|
||||
* Get the length of a vector.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer.
|
||||
* @return Returns the length of the vector whose offset is stored at `offset`.
|
||||
*/
|
||||
public fun vectorLength(offset: Int): Int {
|
||||
var newOffset = offset
|
||||
newOffset += bufferPos
|
||||
newOffset += bb.getInt(newOffset)
|
||||
return bb.getInt(newOffset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start data of a vector.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer.
|
||||
* @return Returns the start of the vector data whose offset is stored at `offset`.
|
||||
*/
|
||||
public fun vector(offset: Int): Int {
|
||||
var newOffset = offset
|
||||
newOffset += bufferPos
|
||||
return newOffset + bb.getInt(newOffset) + Int.SIZE_BYTES // data starts after the length
|
||||
}
|
||||
/**
|
||||
* Initialize vector as a ReadWriteBuffer.
|
||||
*
|
||||
* This is more efficient than using duplicate, since it doesn't copy the data
|
||||
* nor allocates a new [ReadBuffer], creating no garbage to be collected.
|
||||
*
|
||||
* @param buffer The [ReadBuffer] for the array
|
||||
* @param vectorOffset The position of the vector in the byte buffer
|
||||
* @param elemSize The size of each element in the array
|
||||
* @return The [ReadBuffer] for the array
|
||||
*/
|
||||
public fun vectorAsBuffer(buffer: ReadWriteBuffer, vectorOffset: Int, elemSize: Int): ReadBuffer {
|
||||
val o = offset(vectorOffset)
|
||||
if (o == 0) return emptyBuffer
|
||||
val vectorStart = vector(o)
|
||||
return buffer.slice(vectorStart, vectorLength(o) * elemSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize any Table-derived type to point to the union at the given `offset`.
|
||||
*
|
||||
* @param t A `Table`-derived type that should point to the union at `offset`.
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer.
|
||||
* @return Returns the Table that points to the union at `offset`.
|
||||
*/
|
||||
public fun union(t: Table, offset: Int): Table = union(t, offset, bb)
|
||||
|
||||
/**
|
||||
* Sort tables by the key.
|
||||
*
|
||||
* @param offsets An 'int' indexes of the tables into the bb.
|
||||
* @param bb A `ReadWriteBuffer` to get the tables.
|
||||
*/
|
||||
public fun <T> sortTables(offsets: Array<Offset<T>>, bb: ReadWriteBuffer) {
|
||||
val off = offsets.sortedWith { o1, o2 -> keysCompare(o1, o2, bb) }
|
||||
for (i in offsets.indices) offsets[i] = off[i]
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two tables by the key.
|
||||
*
|
||||
* @param o1 An 'Integer' index of the first key into the bb.
|
||||
* @param o2 An 'Integer' index of the second key into the bb.
|
||||
* @param buffer A `ReadWriteBuffer` to get the keys.
|
||||
*/
|
||||
public open fun keysCompare(o1: Offset<*>, o2: Offset<*>, buffer: ReadWriteBuffer): Int = 0
|
||||
|
||||
/**
|
||||
* Re-init the internal state with an external buffer `ReadWriteBuffer` and an offset within.
|
||||
*
|
||||
* This method exists primarily to allow recycling Table instances without risking memory leaks
|
||||
* due to `ReadWriteBuffer` references.
|
||||
*/
|
||||
public inline fun <reified T: Table> reset(i: Int, reuseBuffer: ReadWriteBuffer): T {
|
||||
bb = reuseBuffer
|
||||
if (bb != emptyBuffer) {
|
||||
bufferPos = i
|
||||
vtableStart = bufferPos - bb.getInt(bufferPos)
|
||||
vtableSize = bb.getShort(vtableStart).toInt()
|
||||
} else {
|
||||
bufferPos = 0
|
||||
vtableStart = 0
|
||||
vtableSize = 0
|
||||
}
|
||||
return this as T
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state with a null `ReadWriteBuffer` and a zero position.
|
||||
*
|
||||
* This method exists primarily to allow recycling Table instances without risking memory leaks
|
||||
* due to `ReadWriteBuffer` references. The instance will be unusable until it is assigned
|
||||
* again to a `ReadWriteBuffer`.
|
||||
*/
|
||||
public inline fun <reified T: Table> reset(): T = reset(0, emptyBuffer)
|
||||
|
||||
public companion object {
|
||||
|
||||
public fun offset(vtableOffset: Int, offset: Offset<*>, bb: ReadWriteBuffer): Int {
|
||||
val vtable: Int = bb.capacity - offset.value
|
||||
return bb.getShort(vtable + vtableOffset - bb.getInt(vtable)) + vtable
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a relative offset.
|
||||
*
|
||||
* @param offset An `int` index into a ReadWriteBuffer containing the relative offset.
|
||||
* @param bb from which the relative offset will be retrieved.
|
||||
* @return Returns the relative offset stored at `offset`.
|
||||
*/
|
||||
public fun indirect(offset: Int, bb: ReadWriteBuffer): Int {
|
||||
return offset + bb.getInt(offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Java `String` from UTF-8 data stored inside the FlatBuffer.
|
||||
*
|
||||
* This allocates a new string and converts to wide chars upon each access,
|
||||
* which is not very efficient. Instead, each FlatBuffer string also comes with an
|
||||
* accessor based on __vector_as_ReadWriteBuffer below, which is much more efficient,
|
||||
* assuming your Java program can handle UTF-8 data directly.
|
||||
*
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer.
|
||||
* @param bb Table ReadWriteBuffer used to read a string at given offset.
|
||||
* @return Returns a `String` from the data stored inside the FlatBuffer at `offset`.
|
||||
*/
|
||||
public fun string(offset: Int, bb: ReadWriteBuffer): String {
|
||||
var newOffset = offset
|
||||
newOffset += bb.getInt(newOffset)
|
||||
val length: Int = bb.getInt(newOffset)
|
||||
return bb.getString(newOffset + Int.SIZE_BYTES, length)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize any Table-derived type to point to the union at the given `offset`.
|
||||
*
|
||||
* @param t A `Table`-derived type that should point to the union at `offset`.
|
||||
* @param offset An `int` index into the Table's ReadWriteBuffer.
|
||||
* @param bb Table ReadWriteBuffer used to initialize the object Table-derived type.
|
||||
* @return Returns the Table that points to the union at `offset`.
|
||||
*/
|
||||
public fun union(t: Table, offset: Int, bb: ReadWriteBuffer): Table =
|
||||
t.reset(indirect(offset, bb), bb)
|
||||
|
||||
/**
|
||||
* Check if a [ReadWriteBuffer] contains a file identifier.
|
||||
*
|
||||
* @param bb A `ReadWriteBuffer` to check if it contains the identifier
|
||||
* `ident`.
|
||||
* @param ident A `String` identifier of the FlatBuffer file.
|
||||
* @return True if the buffer contains the file identifier
|
||||
*/
|
||||
public fun hasIdentifier(bb: ReadWriteBuffer?, ident: String): Boolean {
|
||||
val identifierLength = 4
|
||||
if (ident.length != identifierLength)
|
||||
throw AssertionError("FlatBuffers: file identifier must be length $identifierLength")
|
||||
for (i in 0 until identifierLength) {
|
||||
if (ident[i].code.toByte() != bb!![bb.limit + Int.SIZE_BYTES + i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two strings in the buffer.
|
||||
*
|
||||
* @param offsetA An 'int' index of the first string into the bb.
|
||||
* @param offsetB An 'int' index of the second string into the bb.
|
||||
* @param bb A `ReadWriteBuffer` to get the strings.
|
||||
*/
|
||||
public fun compareStrings(offsetA: Int, offsetB: Int, bb: ReadWriteBuffer): Int {
|
||||
var offset1 = offsetA
|
||||
var offset2 = offsetB
|
||||
offset1 += bb.getInt(offset1)
|
||||
offset2 += bb.getInt(offset2)
|
||||
val len1: Int = bb.getInt(offset1)
|
||||
val len2: Int = bb.getInt(offset2)
|
||||
val startPos1: Int = offset1 + Int.SIZE_BYTES
|
||||
val startPos2: Int = offset2 + Int.SIZE_BYTES
|
||||
val len: Int = min(len1, len2)
|
||||
for (i in 0 until len) {
|
||||
if (bb[i + startPos1] != bb[i + startPos2]) {
|
||||
return bb[i + startPos1] - bb[i + startPos2]
|
||||
}
|
||||
}
|
||||
return len1 - len2
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare string from the buffer with the 'String' object.
|
||||
*
|
||||
* @param offset An 'int' index of the first string into the bb.
|
||||
* @param key Second string as a byte array.
|
||||
* @param bb A `ReadWriteBuffer` to get the first string.
|
||||
*/
|
||||
public fun compareStrings(offset: Int, key: ByteArray, bb: ReadWriteBuffer): Int {
|
||||
var offset1 = offset
|
||||
offset1 += bb.getInt(offset1)
|
||||
val len1: Int = bb.getInt(offset1)
|
||||
val len2 = key.size
|
||||
val startPos: Int = offset1 + Int.SIZE_BYTES
|
||||
val len: Int = min(len1, len2)
|
||||
for (i in 0 until len) {
|
||||
if (bb[i + startPos] != key[i]) return bb[i + startPos] - key[i]
|
||||
}
|
||||
return len1 - len2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All structs in the generated code derive from this class, and add their own accessors.
|
||||
*/
|
||||
public open class Struct {
|
||||
/** Used to hold the position of the `bb` buffer. */
|
||||
protected var bufferPos: Int = 0
|
||||
|
||||
/** The underlying ByteBuffer to hold the data of the Struct. */
|
||||
protected var bb: ReadWriteBuffer = emptyBuffer
|
||||
|
||||
/**
|
||||
* Re-init the internal state with an external buffer `ByteBuffer` and an offset within.
|
||||
*
|
||||
* This method exists primarily to allow recycling Table instances without risking memory leaks
|
||||
* due to `ByteBuffer` references.
|
||||
*/
|
||||
protected inline fun <reified T: Struct> reset(i: Int, reuseBuffer: ReadWriteBuffer): T {
|
||||
bb = reuseBuffer
|
||||
bufferPos = if (bb != emptyBuffer) i else 0
|
||||
return this as T
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets internal state with a null `ByteBuffer` and a zero position.
|
||||
*
|
||||
* This method exists primarily to allow recycling Struct instances without risking memory leaks
|
||||
* due to `ByteBuffer` references. The instance will be unusable until it is assigned
|
||||
* again to a `ByteBuffer`.
|
||||
*/
|
||||
private inline fun <reified T: Struct> reset(): T = reset(0, emptyBuffer)
|
||||
}
|
||||
|
||||
public inline val <T> T.value: T get() = this
|
||||
|
||||
public const val VERSION_2_0_8: Int = 1
|
||||
+915
@@ -0,0 +1,915 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
@file:JvmName("FlexBuffers")
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* Reads a FlexBuffer message in ReadBuf and returns [Reference] to
|
||||
* the root element.
|
||||
* @param buffer ReadBuf containing FlexBuffer message
|
||||
* @return [Reference] to the root object
|
||||
*/
|
||||
public fun getRoot(buffer: ReadBuffer): Reference {
|
||||
var end: Int = buffer.limit
|
||||
val byteWidth = buffer[--end].toInt()
|
||||
val packetType = buffer[--end].toInt()
|
||||
end -= byteWidth // The root data item.
|
||||
return Reference(buffer, end, ByteWidth(byteWidth), packetType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an generic element in the buffer. It can be specialized into scalar types, using for example,
|
||||
* [Reference.toInt], or casted into Flexbuffer object types, like [Reference.toMap] or [Reference.toBlob].
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public class Reference internal constructor(
|
||||
internal val buffer: ReadBuffer,
|
||||
internal val end: Int,
|
||||
internal val parentWidth: ByteWidth,
|
||||
internal val byteWidth: ByteWidth,
|
||||
public val type: FlexBufferType
|
||||
) {
|
||||
|
||||
internal constructor(bb: ReadBuffer, end: Int, parentWidth: ByteWidth, packedType: Int) :
|
||||
this(bb, end, parentWidth, ByteWidth(1 shl (packedType and 3)), FlexBufferType((packedType shr 2)))
|
||||
|
||||
/**
|
||||
* Checks whether the element is null type
|
||||
* @return true if null type
|
||||
*/
|
||||
public val isNull: Boolean get() = type == T_NULL
|
||||
|
||||
/**
|
||||
* Checks whether the element is boolean type
|
||||
* @return true if boolean type
|
||||
*/
|
||||
public val isBoolean: Boolean get() = type == T_BOOL
|
||||
|
||||
/**
|
||||
* Checks whether the element type is numeric (signed/unsigned integers and floats)
|
||||
* @return true if numeric type
|
||||
*/
|
||||
public val isNumeric: Boolean get() = isIntOrUInt || isFloat
|
||||
|
||||
/**
|
||||
* Checks whether the element type is signed or unsigned integers
|
||||
* @return true if an integer type
|
||||
*/
|
||||
public val isIntOrUInt: Boolean get() = isInt || isUInt
|
||||
|
||||
/**
|
||||
* Checks whether the element type is float
|
||||
* @return true if a float type
|
||||
*/
|
||||
public val isFloat: Boolean get() = type == T_FLOAT || type == T_INDIRECT_FLOAT
|
||||
|
||||
/**
|
||||
* Checks whether the element type is signed integer
|
||||
* @return true if a signed integer type
|
||||
*/
|
||||
public val isInt: Boolean get() = type == T_INT || type == T_INDIRECT_INT
|
||||
|
||||
/**
|
||||
* Checks whether the element type is signed integer
|
||||
* @return true if a signed integer type
|
||||
*/
|
||||
public val isUInt: Boolean get() = type == T_UINT || type == T_INDIRECT_UINT
|
||||
|
||||
/**
|
||||
* Checks whether the element type is string
|
||||
* @return true if a string type
|
||||
*/
|
||||
public val isString: Boolean get() = type == T_STRING
|
||||
|
||||
/**
|
||||
* Checks whether the element type is key
|
||||
* @return true if a key type
|
||||
*/
|
||||
public val isKey: Boolean get() = type == T_KEY
|
||||
|
||||
/**
|
||||
* Checks whether the element type is vector or a map. [TypedVector] are considered different types and will return
|
||||
* false.
|
||||
* @return true if a vector type
|
||||
*/
|
||||
public val isVector: Boolean get() = type == T_VECTOR || type == T_MAP
|
||||
|
||||
/**
|
||||
* Checks whether the element type is typed vector
|
||||
* @return true if a typed vector type
|
||||
*/
|
||||
public val isTypedVector: Boolean get() = type.isTypedVector()
|
||||
|
||||
/**
|
||||
* Checks whether the element type is a map
|
||||
* @return true if a map type
|
||||
*/
|
||||
public val isMap: Boolean get() = type == T_MAP
|
||||
|
||||
/**
|
||||
* Checks whether the element type is a blob
|
||||
* @return true if a blob type
|
||||
*/
|
||||
public val isBlob: Boolean get() = type == T_BLOB
|
||||
|
||||
/**
|
||||
* Assumes [Reference] as a [Vector] and returns a [Reference] at index [index].
|
||||
*/
|
||||
public operator fun get(index: Int): Reference = toVector()[index]
|
||||
|
||||
/**
|
||||
* Assumes [Reference] as a [Map] and returns a [Reference] for the value at key [key].
|
||||
*/
|
||||
public operator fun get(key: String): Reference = toMap()[key]
|
||||
|
||||
/**
|
||||
* Returns element as a [Boolean].
|
||||
* If element type is not boolean, it will be casted to integer and compared against 0
|
||||
* @return element as [Boolean]
|
||||
*/
|
||||
public fun toBoolean(): Boolean = if (isBoolean) buffer.getBoolean(end) else toUInt() != 0u
|
||||
|
||||
/**
|
||||
* Returns element as [Byte].
|
||||
* For vector types, it will return size of the vector.
|
||||
* For String type, it will be parsed as integer.
|
||||
* Unsigned elements will become signed (with possible overflow).
|
||||
* Float elements will be casted to [Byte].
|
||||
* @return [Byte] or 0 if fail to convert element to integer.
|
||||
*/
|
||||
public fun toByte(): Byte = toULong().toByte()
|
||||
|
||||
/**
|
||||
* Returns element as [Short].
|
||||
* For vector types, it will return size of the vector.
|
||||
* For String type, it will type to be parsed as integer.
|
||||
* Unsigned elements will become signed (with possible overflow).
|
||||
* Float elements will be casted to [Short]
|
||||
* @return [Short] or 0 if fail to convert element to integer.
|
||||
*/
|
||||
public fun toShort(): Short = toULong().toShort()
|
||||
|
||||
/**
|
||||
* Returns element as [Int].
|
||||
* For vector types, it will return size of the vector.
|
||||
* For String type, it will type to be parsed as integer.
|
||||
* Unsigned elements will become signed (with possible overflow).
|
||||
* Float elements will be casted to [Int]
|
||||
* @return [Int] or 0 if fail to convert element to integer.
|
||||
*/
|
||||
public fun toInt(): Int = toULong().toInt()
|
||||
|
||||
/**
|
||||
* Returns element as [Long].
|
||||
* For vector types, it will return size of the vector
|
||||
* For String type, it will type to be parsed as integer
|
||||
* Unsigned elements will become negative
|
||||
* Float elements will be casted to integer
|
||||
* @return [Long] integer or 0 if fail to convert element to long.
|
||||
*/
|
||||
public fun toLong(): Long = toULong().toLong()
|
||||
|
||||
/**
|
||||
* Returns element as [UByte].
|
||||
* For vector types, it will return size of the vector.
|
||||
* For String type, it will type to be parsed as integer.
|
||||
* Negative elements will become unsigned counterpart.
|
||||
* Float elements will be casted to [UByte]
|
||||
* @return [UByte] or 0 if fail to convert element to integer.
|
||||
*/
|
||||
public fun toUByte(): UByte = toULong().toUByte()
|
||||
|
||||
/**
|
||||
* Returns element as [UShort].
|
||||
* For vector types, it will return size of the vector.
|
||||
* For String type, it will type to be parsed as integer.
|
||||
* Negative elements will become unsigned counterpart.
|
||||
* Float elements will be casted to [UShort]
|
||||
* @return [UShort] or 0 if fail to convert element to integer.
|
||||
*/
|
||||
public fun toUShort(): UShort = toULong().toUShort()
|
||||
|
||||
/**
|
||||
* Returns element as [UInt].
|
||||
* For vector types, it will return size of the vector.
|
||||
* For String type, it will type to be parsed as integer.
|
||||
* Negative elements will become unsigned counterpart.
|
||||
* Float elements will be casted to [UInt]
|
||||
* @return [UInt] or 0 if fail to convert element to integer.
|
||||
*/
|
||||
public fun toUInt(): UInt = toULong().toUInt()
|
||||
|
||||
/**
|
||||
* Returns element as [ULong] integer.
|
||||
* For vector types, it will return size of the vector
|
||||
* For String type, it will type to be parsed as integer
|
||||
* Negative elements will become unsigned counterpart.
|
||||
* Float elements will be casted to integer
|
||||
* @return [ULong] integer or 0 if fail to convert element to long.
|
||||
*/
|
||||
public fun toULong(): ULong = resolve { pos: Int, width: ByteWidth ->
|
||||
when (type) {
|
||||
T_INDIRECT_INT, T_INDIRECT_UINT, T_INT, T_BOOL, T_UINT -> buffer.readULong(pos, width)
|
||||
T_FLOAT, T_INDIRECT_FLOAT -> buffer.readFloat(pos, width).toULong()
|
||||
T_STRING -> toString().toULong()
|
||||
T_VECTOR -> toVector().size.toULong()
|
||||
else -> 0UL
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as [Float].
|
||||
* For vector types, it will return size of the vector
|
||||
* For String type, it will type to be parsed as [Float]
|
||||
* Float elements will be casted to integer
|
||||
* @return [Float] integer or 0 if fail to convert element to long.
|
||||
*/
|
||||
public fun toFloat(): Float = resolve { pos: Int, width: ByteWidth ->
|
||||
when (type) {
|
||||
T_INDIRECT_FLOAT, T_FLOAT -> buffer.readFloat(pos, width).toFloat()
|
||||
T_INT -> buffer.readInt(end, parentWidth).toFloat()
|
||||
T_UINT, T_BOOL -> buffer.readUInt(end, parentWidth).toFloat()
|
||||
T_INDIRECT_INT -> buffer.readInt(pos, width).toFloat()
|
||||
T_INDIRECT_UINT -> buffer.readUInt(pos, width).toFloat()
|
||||
T_NULL -> 0.0f
|
||||
T_STRING -> toString().toFloat()
|
||||
T_VECTOR -> toVector().size.toFloat()
|
||||
else -> 0f
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as [Double].
|
||||
* For vector types, it will return size of the vector
|
||||
* For String type, it will type to be parsed as [Double]
|
||||
* @return [Float] integer or 0 if fail to convert element to long.
|
||||
*/
|
||||
public fun toDouble(): Double = resolve { pos: Int, width: ByteWidth ->
|
||||
when (type) {
|
||||
T_INDIRECT_FLOAT, T_FLOAT -> buffer.readFloat(pos, width)
|
||||
T_INT -> buffer.readInt(pos, width).toDouble()
|
||||
T_UINT, T_BOOL -> buffer.readUInt(pos, width).toDouble()
|
||||
T_INDIRECT_INT -> buffer.readInt(pos, width).toDouble()
|
||||
T_INDIRECT_UINT -> buffer.readUInt(pos, width).toDouble()
|
||||
T_NULL -> 0.0
|
||||
T_STRING -> toString().toDouble()
|
||||
T_VECTOR -> toVector().size.toDouble()
|
||||
else -> 0.0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as [Key] or invalid key.
|
||||
*/
|
||||
public fun toKey(): Key = when (type) {
|
||||
T_KEY -> Key(buffer, buffer.indirect(end, parentWidth))
|
||||
else -> nullKey()
|
||||
}
|
||||
/**
|
||||
* Returns element as a [String]
|
||||
* @return element as [String] or empty [String] if fail
|
||||
*/
|
||||
override fun toString(): String = when (type) {
|
||||
T_STRING -> {
|
||||
val start = buffer.indirect(end, parentWidth)
|
||||
val size = buffer.readULong(start - byteWidth, byteWidth).toInt()
|
||||
buffer.getString(start, size)
|
||||
}
|
||||
T_KEY -> buffer.getKeyString(buffer.indirect(end, parentWidth))
|
||||
T_MAP -> "{ ${toMap().entries.joinToString(", ") { "${it.key}: ${it.value}"}} }"
|
||||
T_VECTOR, T_VECTOR_BOOL, T_VECTOR_FLOAT, T_VECTOR_INT,
|
||||
T_VECTOR_UINT, T_VECTOR_KEY, T_VECTOR_STRING_DEPRECATED ->
|
||||
"[ ${toVector().joinToString(", ") { it.toString() }} ]"
|
||||
T_INT -> toLong().toString()
|
||||
T_UINT -> toULong().toString()
|
||||
T_FLOAT -> toDouble().toString()
|
||||
else -> "${type.typeToString()}(end=$end)"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [ByteArray], converting scalar types when possible.
|
||||
* @return element as [ByteArray] or empty [ByteArray] if fail.
|
||||
*/
|
||||
public fun toByteArray(): ByteArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> ByteArray(vec.size) { vec.getInt(it).toByte() }
|
||||
T_VECTOR_UINT -> ByteArray(vec.size) { vec.getUInt(it).toByte() }
|
||||
T_VECTOR -> ByteArray(vec.size) { vec[it].toByte() }
|
||||
T_VECTOR_FLOAT -> ByteArray(vec.size) { vec.getFloat(it).toInt().toByte() }
|
||||
else -> ByteArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [ByteArray], converting scalar types when possible.
|
||||
* @return element as [ByteArray] or empty [ByteArray] if fail.
|
||||
*/
|
||||
public fun toShortArray(): ShortArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> ShortArray(vec.size) { vec.getInt(it).toShort() }
|
||||
T_VECTOR_UINT -> ShortArray(vec.size) { vec.getUInt(it).toShort() }
|
||||
T_VECTOR -> ShortArray(vec.size) { vec[it].toShort() }
|
||||
T_VECTOR_FLOAT -> ShortArray(vec.size) { vec.getFloat(it).toInt().toShort() }
|
||||
else -> ShortArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [IntArray], converting scalar types when possible.
|
||||
* @return element as [IntArray] or empty [IntArray] if fail.
|
||||
*/
|
||||
public fun toIntArray(): IntArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> IntArray(vec.size) { vec.getInt(it).toInt() }
|
||||
T_VECTOR_UINT -> IntArray(vec.size) { vec.getUInt(it).toInt() }
|
||||
T_VECTOR -> IntArray(vec.size) { vec[it].toInt() }
|
||||
T_VECTOR_FLOAT -> IntArray(vec.size) { vec.getFloat(it).toInt() }
|
||||
else -> IntArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [LongArray], converting scalar types when possible.
|
||||
* @return element as [LongArray] or empty [LongArray] if fail.
|
||||
*/
|
||||
public fun toLongArray(): LongArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> LongArray(vec.size) { vec.getInt(it) }
|
||||
T_VECTOR_UINT -> LongArray(vec.size) { vec.getInt(it) }
|
||||
T_VECTOR -> LongArray(vec.size) { vec[it].toLong() }
|
||||
T_VECTOR_FLOAT -> LongArray(vec.size) { vec.getFloat(it).toLong() }
|
||||
else -> LongArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [UByteArray], converting scalar types when possible.
|
||||
* @return element as [UByteArray] or empty [UByteArray] if fail.
|
||||
*/
|
||||
public fun toUByteArray(): UByteArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> UByteArray(vec.size) { vec.getInt(it).toUByte() }
|
||||
T_VECTOR_UINT -> UByteArray(vec.size) { vec.getUInt(it).toUByte() }
|
||||
T_VECTOR -> UByteArray(vec.size) { vec[it].toUByte() }
|
||||
T_VECTOR_FLOAT -> UByteArray(vec.size) { vec.getFloat(it).toInt().toUByte() }
|
||||
else -> UByteArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [UIntArray], converting scalar types when possible.
|
||||
* @return element as [UIntArray] or empty [UIntArray] if fail.
|
||||
*/
|
||||
public fun toUShortArray(): UShortArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> UShortArray(vec.size) { vec.getInt(it).toUShort() }
|
||||
T_VECTOR_UINT -> UShortArray(vec.size) { vec.getUInt(it).toUShort() }
|
||||
T_VECTOR -> UShortArray(vec.size) { vec[it].toUShort() }
|
||||
T_VECTOR_FLOAT -> UShortArray(vec.size) { vec.getFloat(it).toUInt().toUShort() }
|
||||
else -> UShortArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [UIntArray], converting scalar types when possible.
|
||||
* @return element as [UIntArray] or empty [UIntArray] if fail.
|
||||
*/
|
||||
public fun toUIntArray(): UIntArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> UIntArray(vec.size) { vec.getInt(it).toUInt() }
|
||||
T_VECTOR_UINT -> UIntArray(vec.size) { vec.getUInt(it).toUInt() }
|
||||
T_VECTOR -> UIntArray(vec.size) { vec[it].toUInt() }
|
||||
T_VECTOR_FLOAT -> UIntArray(vec.size) { vec.getFloat(it).toUInt() }
|
||||
else -> UIntArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [ULongArray], converting scalar types when possible.
|
||||
* @return element as [ULongArray] or empty [ULongArray] if fail.
|
||||
*/
|
||||
public fun toULongArray(): ULongArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_INT -> ULongArray(vec.size) { vec.getUInt(it) }
|
||||
T_VECTOR_UINT -> ULongArray(vec.size) { vec.getUInt(it) }
|
||||
T_VECTOR -> ULongArray(vec.size) { vec[it].toULong() }
|
||||
T_VECTOR_FLOAT -> ULongArray(vec.size) { vec.getFloat(it).toULong() }
|
||||
else -> ULongArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [FloatArray], converting scalar types when possible.
|
||||
* @return element as [FloatArray] or empty [FloatArray] if fail.
|
||||
*/
|
||||
public fun toFloatArray(): FloatArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_FLOAT -> FloatArray(vec.size) { vec.getFloat(it).toFloat() }
|
||||
T_VECTOR_INT -> FloatArray(vec.size) { vec.getInt(it).toFloat() }
|
||||
T_VECTOR_UINT -> FloatArray(vec.size) { vec.getUInt(it).toFloat() }
|
||||
T_VECTOR -> FloatArray(vec.size) { vec[it].toFloat() }
|
||||
else -> FloatArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [DoubleArray], converting scalar types when possible.
|
||||
* @return element as [DoubleArray] or empty [DoubleArray] if fail.
|
||||
*/
|
||||
public fun toDoubleArray(): DoubleArray {
|
||||
val vec = TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
return when (type) {
|
||||
T_VECTOR_FLOAT -> DoubleArray(vec.size) { vec[it].toDouble() }
|
||||
T_VECTOR_INT -> DoubleArray(vec.size) { vec[it].toDouble() }
|
||||
T_VECTOR_UINT -> DoubleArray(vec.size) { vec[it].toDouble() }
|
||||
T_VECTOR -> DoubleArray(vec.size) { vec[it].toDouble() }
|
||||
else -> DoubleArray(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [Vector]
|
||||
* @return element as [Vector] or empty [Vector] if fail
|
||||
*/
|
||||
public fun toVector(): Vector {
|
||||
return when {
|
||||
isVector -> Vector(buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
isTypedVector -> TypedVector(type.toElementTypedVector(), buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
else -> emptyVector()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [Blob]
|
||||
* @return element as [Blob] or empty [Blob] if fail
|
||||
*/
|
||||
public fun toBlob(): Blob {
|
||||
return when (type) {
|
||||
T_BLOB, T_STRING -> Blob(buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
else -> emptyBlob()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element as a [Map].
|
||||
* @return element as [Map] or empty [Map] if fail
|
||||
*/
|
||||
public fun toMap(): Map = when (type) {
|
||||
T_MAP -> Map(buffer, buffer.indirect(end, parentWidth), byteWidth)
|
||||
else -> emptyMap()
|
||||
}
|
||||
|
||||
private inline fun <T> resolve(crossinline block: (pos: Int, width: ByteWidth) -> T): T {
|
||||
return if (type.isIndirectScalar()) {
|
||||
block(buffer.indirect(end, byteWidth), byteWidth)
|
||||
} else {
|
||||
block(end, parentWidth)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
other as Reference
|
||||
if (buffer != other.buffer ||
|
||||
end != other.end ||
|
||||
parentWidth != other.parentWidth ||
|
||||
byteWidth != other.byteWidth ||
|
||||
type != other.type
|
||||
) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = buffer.hashCode()
|
||||
result = 31 * result + end
|
||||
result = 31 * result + parentWidth.value
|
||||
result = 31 * result + byteWidth.value
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents any element that has a size property to it, like: [Map], [Vector] and [TypedVector].
|
||||
*/
|
||||
public open class Sized internal constructor(
|
||||
public val buffer: ReadBuffer,
|
||||
public val end: Int,
|
||||
public val byteWidth: ByteWidth
|
||||
) {
|
||||
public open val size: Int = buffer.readSize(end, byteWidth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent an array of bytes in the buffer.
|
||||
*/
|
||||
public open class Blob internal constructor(
|
||||
buffer: ReadBuffer,
|
||||
end: Int,
|
||||
byteWidth: ByteWidth
|
||||
) : Sized(buffer, end, byteWidth) {
|
||||
/**
|
||||
* Return [Blob] as [ReadBuffer]
|
||||
* @return blob as [ReadBuffer]
|
||||
*/
|
||||
public fun data(): ReadBuffer = buffer.slice(end, size)
|
||||
|
||||
/**
|
||||
* Copy [Blob] into a [ByteArray]
|
||||
* @return A [ByteArray] containing the blob data.
|
||||
*/
|
||||
public fun toByteArray(): ByteArray {
|
||||
val result = ByteArray(size)
|
||||
for (i in 0 until size) {
|
||||
result[i] = buffer[end + i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Return individual byte at a given position
|
||||
* @param pos position of the byte to be read
|
||||
*/
|
||||
public operator fun get(pos: Int): Byte {
|
||||
if (pos !in 0..size) error("$pos index out of bounds. Should be in range 0..$size")
|
||||
return buffer[end + pos]
|
||||
}
|
||||
|
||||
override fun toString(): String = buffer.getString(end, size)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Vector] represents an array of elements in the buffer. The element can be of any type.
|
||||
*/
|
||||
public open class Vector internal constructor(
|
||||
buffer: ReadBuffer,
|
||||
end: Int,
|
||||
byteWidth: ByteWidth
|
||||
) : Collection<Reference>,
|
||||
Sized(buffer, end, byteWidth) {
|
||||
|
||||
/**
|
||||
* Returns a [Reference] from the [Vector] at position [index]. Returns a null reference
|
||||
* @param index position in the vector.
|
||||
* @return [Reference] for a key or a null [Reference] if not found.
|
||||
*/
|
||||
public open operator fun get(index: Int): Reference {
|
||||
if (index >= size) return nullReference()
|
||||
val packedType = buffer[(end + size * byteWidth.value + index)].toInt()
|
||||
val objEnd = end + index * byteWidth
|
||||
return Reference(buffer, objEnd, byteWidth, packedType)
|
||||
}
|
||||
|
||||
// overrides from Collection<Reference>
|
||||
|
||||
override fun contains(element: Reference): Boolean = find { it == element } != null
|
||||
|
||||
override fun containsAll(elements: Collection<Reference>): Boolean {
|
||||
elements.forEach { if (!contains(it)) return false }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean = size == 0
|
||||
|
||||
override fun iterator(): Iterator<Reference> = object : Iterator<Reference> {
|
||||
var position = 0
|
||||
override fun hasNext(): Boolean = position != size
|
||||
override fun next(): Reference = get(position++)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [TypedVector] represents an array of scalar elements of the same type in the buffer.
|
||||
*/
|
||||
public open class TypedVector(
|
||||
private val elementType: FlexBufferType,
|
||||
buffer: ReadBuffer,
|
||||
end: Int,
|
||||
byteWidth: ByteWidth
|
||||
) : Vector(buffer, end, byteWidth) {
|
||||
|
||||
/**
|
||||
* Returns a [Reference] from the [TypedVector] at position [index]. Returns a null reference
|
||||
* @param index position in the vector.
|
||||
* @return [Reference] for a key or a null [Reference] if not found.
|
||||
*/
|
||||
override operator fun get(index: Int): Reference {
|
||||
if (index >= size) return nullReference()
|
||||
val childPos: Int = end + index * byteWidth
|
||||
return Reference(buffer, childPos, byteWidth, ByteWidth(1), elementType)
|
||||
}
|
||||
|
||||
private inline fun <T> resolveAt(index: Int, crossinline block: (Int, ByteWidth) -> T): T {
|
||||
val childPos: Int = end + index * byteWidth
|
||||
return block(childPos, byteWidth)
|
||||
}
|
||||
|
||||
internal fun getBoolean(index: Int): Boolean = resolveAt(index) {
|
||||
pos: Int, _: ByteWidth -> buffer.getBoolean(pos)
|
||||
}
|
||||
internal fun getInt(index: Int): Long = resolveAt(index) {
|
||||
pos: Int, width: ByteWidth -> buffer.readLong(pos, width)
|
||||
}
|
||||
internal fun getUInt(index: Int): ULong = resolveAt(index) {
|
||||
pos: Int, width: ByteWidth -> buffer.readULong(pos, width)
|
||||
}
|
||||
internal fun getFloat(index: Int): Double = resolveAt(index) {
|
||||
pos: Int, width: ByteWidth -> buffer.readFloat(pos, width)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a key element in the buffer. Keys are
|
||||
* used to reference objects in a [Map]
|
||||
*/
|
||||
public data class Key(
|
||||
public val buffer: ReadBuffer,
|
||||
public val start: Int,
|
||||
public val end: Int = buffer.findFirst(ZeroByte, start)
|
||||
) {
|
||||
|
||||
val sizeInBytes: Int = end - start
|
||||
|
||||
private val codePoint = CharArray(2)
|
||||
|
||||
val sizeInChars: Int
|
||||
get() {
|
||||
var count = 0
|
||||
var i = start
|
||||
while (i < end) {
|
||||
val size = codePointSizeInBytes(i)
|
||||
i += size
|
||||
count += if (size == 4) 2 else 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
public operator fun get(index: Int): Char {
|
||||
var count = 0
|
||||
var i = start
|
||||
var size = 0
|
||||
// we loop over the bytes to find the right position for the "char" at index i
|
||||
while (i < end && count < index) {
|
||||
size = codePointSizeInBytes(i)
|
||||
i += size
|
||||
// 4 bytes utf8 are 2 chars wide, the rest is on char.
|
||||
count += if (size == 4) 2 else 1
|
||||
}
|
||||
return when {
|
||||
count == index -> {
|
||||
Utf8.decodeUtf8CodePoint(buffer, i, codePoint)
|
||||
codePoint[0]
|
||||
}
|
||||
count == index + 1 && size == 4 -> {
|
||||
Utf8.decodeUtf8CodePoint(buffer, i - size, codePoint)
|
||||
codePoint[1]
|
||||
}
|
||||
else -> error("Invalid count=$count, index=$index")
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun codePointSizeInBytes(pos: Int): Int {
|
||||
val b = buffer[pos]
|
||||
return when {
|
||||
Utf8.isOneByte(b) -> 1
|
||||
Utf8.isTwoBytes(b) -> 2
|
||||
Utf8.isThreeBytes(b) -> 3
|
||||
else -> 4
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = if (sizeInBytes > 0) buffer.getString(start, sizeInBytes) else ""
|
||||
|
||||
/**
|
||||
* Checks whether Key is invalid or not.
|
||||
*/
|
||||
public fun isInvalid(): Boolean = sizeInBytes <= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* A Map class that provide support to access Key-Value data from Flexbuffers.
|
||||
*/
|
||||
public class Map
|
||||
internal constructor(buffer: ReadBuffer, end: Int, byteWidth: ByteWidth):
|
||||
Sized(buffer, end, byteWidth),
|
||||
kotlin.collections.Map<Key, Reference> {
|
||||
|
||||
// used for accessing the key vector elements
|
||||
private var keyVectorEnd: Int
|
||||
private var keyVectorByteWidth: ByteWidth
|
||||
init {
|
||||
val keysOffset = end - (3 * byteWidth) // 3 is number of prefixed fields
|
||||
keyVectorEnd = buffer.indirect(keysOffset, byteWidth)
|
||||
keyVectorByteWidth = ByteWidth(buffer.readInt(keysOffset + byteWidth, byteWidth))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Reference] from the [Map] at position [index]. Returns a null reference
|
||||
* @param index position in the map
|
||||
* @return [Reference] for a key or a null [Reference] if not found.
|
||||
*/
|
||||
public operator fun get(index: Int): Reference {
|
||||
if (index >= size) return nullReference()
|
||||
val packedPos = end + size * byteWidth + index
|
||||
val packedType = buffer[packedPos].toInt()
|
||||
val objEnd = end + index * byteWidth
|
||||
return Reference(buffer, objEnd, byteWidth, packedType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Reference] from the [Map] for a given [String] [key].
|
||||
* @param key access key to element on map
|
||||
* @return [Reference] for a key or a null [Reference] if not found.
|
||||
*/
|
||||
public operator fun get(key: String): Reference {
|
||||
val index: Int = binarySearch(key)
|
||||
return if (index in 0 until size) {
|
||||
get(index)
|
||||
} else nullReference()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Reference] from the [Map] for a given [Key] [key].
|
||||
* @param key access key to element on map
|
||||
* @return [Reference] for a key or a null [Reference] if not found.
|
||||
*/
|
||||
override operator fun get(key: Key): Reference {
|
||||
val index = binarySearch(key)
|
||||
return if (index in 0 until size) {
|
||||
get(index)
|
||||
} else nullReference()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the map contains a [key].
|
||||
* @param key [String]
|
||||
* @return true if key is found in the map, otherwise false.
|
||||
*/
|
||||
public operator fun contains(key: String): Boolean = binarySearch(key) >= 0
|
||||
|
||||
/**
|
||||
* Returns a [Key] for a given position [index] in the [Map].
|
||||
* @param index of the key in the map
|
||||
* @return a Key for the given index. Out of bounds indexes returns invalid keys.
|
||||
*/
|
||||
public fun keyAt(index: Int): Key {
|
||||
val childPos: Int = keyVectorEnd + index * keyVectorByteWidth
|
||||
return Key(buffer, buffer.indirect(childPos, keyVectorByteWidth))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Key] as [String] for a given position [index] in the [Map].
|
||||
* @param index of the key in the map
|
||||
* @return a Key for the given index. Out of bounds indexes returns empty string.
|
||||
*/
|
||||
public fun keyAsString(index: Int): String {
|
||||
val childPos: Int = keyVectorEnd + index * keyVectorByteWidth
|
||||
val start = buffer.indirect(childPos, keyVectorByteWidth)
|
||||
val end = buffer.findFirst(ZeroByte, start)
|
||||
return if (end > start) buffer.getString(start, end - start) else ""
|
||||
}
|
||||
|
||||
// Overrides from kotlin.collections.Map<Key, Reference>
|
||||
|
||||
public data class Entry(override val key: Key, override val value: Reference) :
|
||||
kotlin.collections.Map.Entry<Key, Reference>
|
||||
|
||||
override val entries: Set<kotlin.collections.Map.Entry<Key, Reference>>
|
||||
get() = keys.map { Entry(it, get(it.toString())) }.toSet()
|
||||
|
||||
override val keys: Set<Key>
|
||||
get() {
|
||||
val set = LinkedHashSet<Key>(size)
|
||||
for (i in 0 until size) {
|
||||
val key = keyAt(i)
|
||||
set.add(key)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Vector] for accessing all values in the [Map].
|
||||
* @return [Vector] of values.
|
||||
*/
|
||||
override val values: Collection<Reference>
|
||||
get() = Vector(buffer, end, byteWidth)
|
||||
|
||||
override fun containsKey(key: Key): Boolean {
|
||||
for (i in 0 until size) {
|
||||
if (key == keyAt(i))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun containsValue(value: Reference): Boolean = values.contains(value)
|
||||
|
||||
override fun isEmpty(): Boolean = size == 0
|
||||
|
||||
// Performs a binary search on a key vector and return index of the key in key vector
|
||||
private fun binarySearch(searchedKey: String) = binarySearch { compareCharSequence(it, searchedKey) }
|
||||
// Performs a binary search on a key vector and return index of the key in key vector
|
||||
private fun binarySearch(key: Key): Int = binarySearch { compareKeys(it, key.start) }
|
||||
|
||||
private inline fun binarySearch(crossinline comparisonBlock: (Int) -> Int): Int {
|
||||
var low = 0
|
||||
var high = size - 1
|
||||
while (low <= high) {
|
||||
val mid = low + high ushr 1
|
||||
val keyPos: Int = buffer.indirect(keyVectorEnd + mid * keyVectorByteWidth, keyVectorByteWidth)
|
||||
val cmp: Int = comparisonBlock(keyPos)
|
||||
if (cmp < 0) low = mid + 1 else if (cmp > 0) high = mid - 1 else return mid // key found
|
||||
}
|
||||
return -(low + 1) // key not found
|
||||
}
|
||||
|
||||
// compares a CharSequence against a T_KEY
|
||||
private fun compareKeys(start: Int, other: Int): Int {
|
||||
var bufferPos = start
|
||||
var otherPos = other
|
||||
val limit: Int = buffer.limit
|
||||
var c1: Byte = ZeroByte
|
||||
var c2: Byte = ZeroByte
|
||||
while (otherPos < limit) {
|
||||
c1 = buffer[bufferPos++]
|
||||
c2 = buffer[otherPos++]
|
||||
when {
|
||||
c1 == ZeroByte -> return c1 - c2
|
||||
c1 != c2 -> return c1 - c2
|
||||
}
|
||||
}
|
||||
return c1 - c2
|
||||
}
|
||||
|
||||
// compares a CharSequence against a [CharSequence]
|
||||
private fun compareCharSequence(start: Int, other: CharSequence): Int {
|
||||
var bufferPos = start
|
||||
var otherPos = 0
|
||||
val limit: Int = buffer.limit
|
||||
val otherLimit = other.length
|
||||
// special loop for ASCII characters. Most of keys should be ASCII only, so this
|
||||
// loop should be optimized for that.
|
||||
// breaks if a multi-byte character is found
|
||||
while (otherPos < otherLimit) {
|
||||
val c2 = other[otherPos]
|
||||
// not a single byte codepoint
|
||||
if (c2.code >= 0x80) {
|
||||
break
|
||||
}
|
||||
val b: Byte = buffer[bufferPos]
|
||||
when {
|
||||
b == ZeroByte -> return -c2.code
|
||||
b < 0 -> break
|
||||
b != c2.code.toByte() -> return b - c2.code.toByte()
|
||||
}
|
||||
++bufferPos
|
||||
++otherPos
|
||||
}
|
||||
if (bufferPos < limit)
|
||||
return 0
|
||||
|
||||
val comparisonBuffer = ByteArray(4)
|
||||
while (bufferPos < limit) {
|
||||
val sizeInBuff = Utf8.encodeUtf8CodePoint(other, otherPos, comparisonBuffer)
|
||||
if (sizeInBuff == 0) {
|
||||
return buffer[bufferPos].toInt()
|
||||
}
|
||||
for (i in 0 until sizeInBuff) {
|
||||
val bufferByte: Byte = buffer[bufferPos++]
|
||||
val otherByte: Byte = comparisonBuffer[i]
|
||||
when {
|
||||
bufferByte == ZeroByte -> return -otherByte
|
||||
bufferByte != otherByte -> return bufferByte - otherByte
|
||||
}
|
||||
}
|
||||
otherPos += if (sizeInBuff == 4) 2 else 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
+786
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
public class FlexBuffersBuilder(
|
||||
public val buffer: ReadWriteBuffer,
|
||||
private val shareFlag: Int = SHARE_KEYS
|
||||
) {
|
||||
|
||||
public constructor(initialCapacity: Int = 1024, shareFlag: Int = SHARE_KEYS) :
|
||||
this(ArrayReadWriteBuffer(initialCapacity), shareFlag)
|
||||
|
||||
private val stringValuePool: HashMap<String, Value> = HashMap()
|
||||
private val stringKeyPool: HashMap<String, Int> = HashMap()
|
||||
private val stack: MutableList<Value> = mutableListOf()
|
||||
private var finished: Boolean = false
|
||||
|
||||
/**
|
||||
* Reset the FlexBuffersBuilder by purging all data that it holds. Buffer might
|
||||
* keep its capacity after a reset.
|
||||
*/
|
||||
public fun clear() {
|
||||
buffer.clear()
|
||||
stringValuePool.clear()
|
||||
stringKeyPool.clear()
|
||||
stack.clear()
|
||||
finished = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish writing the message into the buffer. After that no other element must
|
||||
* be inserted into the buffer. Also, you must call this function before start using the
|
||||
* FlexBuffer message
|
||||
* @return [ReadBuffer] containing the FlexBuffer message
|
||||
*/
|
||||
public fun finish(): ReadBuffer {
|
||||
// If you hit this, you likely have objects that were never included
|
||||
// in a parent. You need to have exactly one root to finish a buffer.
|
||||
// Check your Start/End calls are matched, and all objects are inside
|
||||
// some other object.
|
||||
if (stack.size != 1) error("There is must be only on object as root. Current ${stack.size}.")
|
||||
// Write root value.
|
||||
val byteWidth = align(stack[0].elemWidth(buffer.writePosition, 0))
|
||||
buffer.requestAdditionalCapacity(byteWidth.value + 2)
|
||||
writeAny(stack[0], byteWidth)
|
||||
// Write root type.
|
||||
buffer.put(stack[0].storedPackedType())
|
||||
// Write root size. Normally determined by parent, but root has no parent :)
|
||||
buffer.put(byteWidth.value.toByte())
|
||||
this.finished = true
|
||||
return buffer // TODO: make a read-only shallow copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a single [Boolean] into the buffer
|
||||
* @param value true or false
|
||||
*/
|
||||
public fun put(value: Boolean): Unit = run { this[null] = value }
|
||||
|
||||
/**
|
||||
* Insert a null reference into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public fun putNull(key: String? = null): Unit =
|
||||
run { stack.add(Value(T_NULL, putKey(key), W_8, 0UL)) }
|
||||
|
||||
/**
|
||||
* Insert a single [Boolean] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: Boolean): Unit =
|
||||
run { stack.add(Value(T_BOOL, putKey(key), W_8, if (value) 1UL else 0UL)) }
|
||||
|
||||
/**
|
||||
* Insert a single [Byte] into the buffer
|
||||
*/
|
||||
public fun put(value: Byte): Unit = set(null, value.toLong())
|
||||
|
||||
/**
|
||||
* Insert a single [Byte] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: Byte): Unit = set(key, value.toLong())
|
||||
|
||||
/**
|
||||
* Insert a single [Short] into the buffer.
|
||||
*/
|
||||
public fun put(value: Short): Unit = set(null, value.toLong())
|
||||
|
||||
/**
|
||||
* Insert a single [Short] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public inline operator fun set(key: String? = null, value: Short): Unit = set(key, value.toLong())
|
||||
|
||||
/**
|
||||
* Insert a single [Int] into the buffer.
|
||||
*/
|
||||
public fun put(value: Int): Unit = set(null, value.toLong())
|
||||
|
||||
/**
|
||||
* Insert a single [Int] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public inline operator fun set(key: String? = null, value: Int): Unit = set(key, value.toLong())
|
||||
|
||||
/**
|
||||
* Insert a single [Long] into the buffer.
|
||||
*/
|
||||
public fun put(value: Long): Unit = set(null, value)
|
||||
|
||||
/**
|
||||
* Insert a single [Long] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: Long): Unit =
|
||||
run { stack.add(Value(T_INT, putKey(key), value.toULong().widthInUBits(), value.toULong())) }
|
||||
|
||||
/**
|
||||
* Insert a single [UByte] into the buffer
|
||||
*/
|
||||
public fun put(value: UByte): Unit = set(null, value.toULong())
|
||||
|
||||
/**
|
||||
* Insert a single [UByte] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public inline operator fun set(key: String? = null, value: UByte): Unit = set(key, value.toULong())
|
||||
|
||||
/**
|
||||
* Insert a single [UShort] into the buffer.
|
||||
*/
|
||||
public fun put(value: UShort): Unit = set(null, value.toULong())
|
||||
|
||||
/**
|
||||
* Insert a single [UShort] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
private inline operator fun set(key: String? = null, value: UShort): Unit = set(key, value.toULong())
|
||||
|
||||
/**
|
||||
* Insert a single [UInt] into the buffer.
|
||||
*/
|
||||
public fun put(value: UInt): Unit = set(null, value.toULong())
|
||||
|
||||
/**
|
||||
* Insert a single [UInt] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
private inline operator fun set(key: String? = null, value: UInt): Unit = set(key, value.toULong())
|
||||
|
||||
/**
|
||||
* Insert a single [ULong] into the buffer.
|
||||
*/
|
||||
public fun put(value: ULong): Unit = set(null, value)
|
||||
|
||||
/**
|
||||
* Insert a single [ULong] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: ULong): Unit =
|
||||
run { stack.add(Value(T_UINT, putKey(key), value.widthInUBits(), value)) }
|
||||
|
||||
/**
|
||||
* Insert a single [Float] into the buffer.
|
||||
*/
|
||||
public fun put(value: Float): Unit = run { this[null] = value }
|
||||
|
||||
/**
|
||||
* Insert a single [Float] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: Float): Unit =
|
||||
run { stack.add(Value(T_FLOAT, putKey(key), W_32, dValue = value.toDouble())) }
|
||||
|
||||
/**
|
||||
* Insert a single [Double] into the buffer.
|
||||
*/
|
||||
public fun put(value: Double): Unit = run { this[null] = value }
|
||||
|
||||
/**
|
||||
* Insert a single [Double] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: Double): Unit =
|
||||
run { stack.add(Value(T_FLOAT, putKey(key), W_64, dValue = value)) }
|
||||
|
||||
/**
|
||||
* Insert a single [String] into the buffer.
|
||||
*/
|
||||
public fun put(value: String): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Insert a single [String] into the buffer. A key must be present if element is inserted into a map.
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: String): Int {
|
||||
val iKey = putKey(key)
|
||||
val holder = if (shareFlag and SHARE_STRINGS != 0) {
|
||||
stringValuePool.getOrPut(value) {
|
||||
writeString(iKey, value).also { stringValuePool[value] = it }
|
||||
}.copy(key = iKey)
|
||||
} else {
|
||||
writeString(iKey, value)
|
||||
}
|
||||
stack.add(holder)
|
||||
return holder.iValue.toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a [ByteArray] into the message as a [Blob].
|
||||
* @param value byte array
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: ByteArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [ByteArray] into the message as a [Blob]. A key must be present if element is inserted into a map.
|
||||
* @param value byte array
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: ByteArray): Int {
|
||||
val element = writeBlob(putKey(key), value, T_BLOB, false)
|
||||
stack.add(element)
|
||||
return element.iValue.toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a [IntArray] into the message as a typed vector of fixed size.
|
||||
* @param value [IntArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: IntArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [IntArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [IntArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: IntArray): Int =
|
||||
setTypedVector(key, value.size, T_VECTOR_INT, value.widthInUBits()) { writeIntArray(value, it) }
|
||||
|
||||
/**
|
||||
* Adds a [ShortArray] into the message as a typed vector of fixed size.
|
||||
* @param value [ShortArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: ShortArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [ShortArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [ShortArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: ShortArray): Int =
|
||||
setTypedVector(key, value.size, T_VECTOR_INT, value.widthInUBits()) { writeIntArray(value, it) }
|
||||
|
||||
/**
|
||||
* Adds a [LongArray] into the message as a typed vector of fixed size.
|
||||
* @param value [LongArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: LongArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [LongArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [LongArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: LongArray): Int =
|
||||
setTypedVector(key, value.size, T_VECTOR_INT, value.widthInUBits()) { writeIntArray(value, it) }
|
||||
|
||||
/**
|
||||
* Adds a [FloatArray] into the message as a typed vector of fixed size.
|
||||
* @param value [FloatArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: FloatArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [FloatArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [FloatArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: FloatArray): Int =
|
||||
setTypedVector(key, value.size, T_VECTOR_FLOAT, W_32) { writeFloatArray(value) }
|
||||
|
||||
/**
|
||||
* Adds a [DoubleArray] into the message as a typed vector of fixed size.
|
||||
* @param value [DoubleArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: DoubleArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [DoubleArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [DoubleArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: DoubleArray): Int =
|
||||
setTypedVector(key, value.size, T_VECTOR_FLOAT, W_64) { writeFloatArray(value) }
|
||||
|
||||
/**
|
||||
* Adds a [UByteArray] into the message as a typed vector of fixed size.
|
||||
* @param value [UByteArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: UByteArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [UByteArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [UByteArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: UByteArray): Int =
|
||||
setTypedVec(key) { value.forEach { put(it) } }
|
||||
|
||||
/**
|
||||
* Adds a [UShortArray] into the message as a typed vector of fixed size.
|
||||
* @param value [UShortArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: UShortArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [UShortArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [UShortArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: UShortArray): Int =
|
||||
setTypedVec(key) { value.forEach { put(it) } }
|
||||
|
||||
/**
|
||||
* Adds a [UIntArray] into the message as a typed vector of fixed size.
|
||||
* @param value [UIntArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: UIntArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [UIntArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [UIntArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun set(key: String? = null, value: UIntArray): Int =
|
||||
setTypedVec(key) { value.forEach { put(it) } }
|
||||
|
||||
/**
|
||||
* Adds a [ULongArray] into the message as a typed vector of fixed size.
|
||||
* @param value [ULongArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public fun put(value: ULongArray): Int = set(null, value)
|
||||
|
||||
/**
|
||||
* Adds a [ULongArray] into the message as a typed vector of fixed size.
|
||||
* A key must be present if element is inserted into a map.
|
||||
* @param value [ULongArray]
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public operator fun set(key: String? = null, value: ULongArray): Int =
|
||||
setTypedVec(key) { value.forEach { put(it) } }
|
||||
|
||||
/**
|
||||
* Creates a new vector will all elements inserted in [block].
|
||||
* @param block where elements will be inserted
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public inline fun putVector(crossinline block: FlexBuffersBuilder.() -> Unit): Int {
|
||||
val pos = startVector()
|
||||
this.block()
|
||||
return endVector(pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new typed vector will all elements inserted in [block].
|
||||
* @param block where elements will be inserted
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public inline fun putTypedVector(crossinline block: FlexBuffersBuilder.() -> Unit): Int {
|
||||
val pos = startVector()
|
||||
this.block()
|
||||
return endTypedVector(pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return position for starting a new vector.
|
||||
*/
|
||||
public fun startVector(): Int = stack.size
|
||||
|
||||
/**
|
||||
* Finishes a vector element. The initial position of the vector must be passed
|
||||
* @param position position at the start of the vector
|
||||
*/
|
||||
public fun endVector(position: Int): Int = endVector(null, position)
|
||||
|
||||
/**
|
||||
* Finishes a vector element. The initial position of the vector must be passed
|
||||
* @param position position at the start of the vector
|
||||
*/
|
||||
public fun endVector(key: String? = null, position: Int): Int =
|
||||
endAnyVector(position) { createVector(putKey(key), position, stack.size - position) }
|
||||
/**
|
||||
* Finishes a typed vector element. The initial position of the vector must be passed
|
||||
* @param position position at the start of the vector
|
||||
*/
|
||||
public fun endTypedVector(position: Int): Int = endTypedVector(position, null)
|
||||
|
||||
/**
|
||||
* Helper function to return position for starting a new vector.
|
||||
*/
|
||||
public fun startMap(): Int = stack.size
|
||||
|
||||
/**
|
||||
* Creates a new map will all elements inserted in [block].
|
||||
* @param block where elements will be inserted
|
||||
* @return position in buffer as the start of byte array
|
||||
*/
|
||||
public inline fun putMap(key: String? = null, crossinline block: FlexBuffersBuilder.() -> Unit): Int {
|
||||
val pos = startMap()
|
||||
this.block()
|
||||
return endMap(pos, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes a map, but writing the information in the buffer
|
||||
* @param key key used to store element in map
|
||||
* @return Reference to the map
|
||||
*/
|
||||
public fun endMap(start: Int, key: String? = null): Int {
|
||||
stack.subList(start, stack.size).sortWith(keyComparator)
|
||||
val length = stack.size - start
|
||||
val keys = createKeyVector(start, length)
|
||||
val vec = putMap(putKey(key), start, length, keys)
|
||||
// Remove temp elements and return map.
|
||||
while (stack.size > start) {
|
||||
stack.removeAt(stack.size - 1)
|
||||
}
|
||||
stack.add(vec)
|
||||
return vec.iValue.toInt()
|
||||
}
|
||||
|
||||
private inline fun setTypedVector(
|
||||
key: String? = null,
|
||||
length: Int,
|
||||
vecType: FlexBufferType,
|
||||
bitWidth: BitWidth,
|
||||
crossinline writeBlock: (ByteWidth) -> Unit
|
||||
): Int {
|
||||
val keyPos = putKey(key)
|
||||
val byteWidth = align(bitWidth)
|
||||
// Write vector. First the keys width/offset if available, and size.
|
||||
// write the size
|
||||
writeInt(length, byteWidth)
|
||||
|
||||
// Then the actual data.
|
||||
val vloc: Int = buffer.writePosition
|
||||
writeBlock(byteWidth)
|
||||
stack.add(Value(vecType, keyPos, bitWidth, vloc.toULong()))
|
||||
return vloc
|
||||
}
|
||||
|
||||
private inline fun setTypedVec(key: String? = null, crossinline block: FlexBuffersBuilder.() -> Unit): Int {
|
||||
val pos = startVector()
|
||||
this.block()
|
||||
return endTypedVector(pos, key)
|
||||
}
|
||||
|
||||
public fun endTypedVector(position: Int, key: String? = null): Int =
|
||||
endAnyVector(position) { createTypedVector(putKey(key), position, stack.size - position) }
|
||||
|
||||
private inline fun endAnyVector(start: Int, crossinline creationBlock: () -> Value): Int {
|
||||
val vec = creationBlock()
|
||||
// Remove temp elements and return vector.
|
||||
while (stack.size > start) {
|
||||
stack.removeLast()
|
||||
}
|
||||
stack.add(vec)
|
||||
return vec.iValue.toInt()
|
||||
}
|
||||
|
||||
private inline fun putKey(key: String? = null): Int {
|
||||
if (key == null) return -1
|
||||
return if ((shareFlag and SHARE_KEYS) != 0) {
|
||||
stringKeyPool.getOrPut(key) {
|
||||
val pos: Int = buffer.writePosition
|
||||
val encodedKeySize = Utf8.encodedLength(key)
|
||||
buffer.requestAdditionalCapacity(encodedKeySize + 1)
|
||||
buffer.put(key, encodedKeySize)
|
||||
buffer.put(ZeroByte)
|
||||
pos
|
||||
}
|
||||
} else {
|
||||
val pos: Int = buffer.writePosition
|
||||
val encodedKeySize = Utf8.encodedLength(key)
|
||||
buffer.requestAdditionalCapacity(encodedKeySize + 1)
|
||||
buffer.put(key, encodedKeySize)
|
||||
buffer.put(ZeroByte)
|
||||
pos
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeAny(toWrite: Value, byteWidth: ByteWidth) = when (toWrite.type) {
|
||||
T_NULL, T_BOOL, T_INT, T_UINT -> writeInt(toWrite.iValue, byteWidth)
|
||||
T_FLOAT -> writeDouble(toWrite.dValue, byteWidth)
|
||||
else -> writeOffset(toWrite.iValue.toInt(), byteWidth)
|
||||
}
|
||||
|
||||
private fun writeString(key: Int, s: String): Value {
|
||||
val encodedSize = Utf8.encodedLength(s)
|
||||
val bitWidth = encodedSize.toULong().widthInUBits()
|
||||
val byteWidth = align(bitWidth)
|
||||
|
||||
writeInt(encodedSize, byteWidth)
|
||||
|
||||
buffer.requestAdditionalCapacity(encodedSize + 1)
|
||||
val sloc: Int = buffer.writePosition
|
||||
if (encodedSize > 0)
|
||||
buffer.put(s, encodedSize)
|
||||
buffer.put(ZeroByte)
|
||||
return Value(T_STRING, key, bitWidth, sloc.toULong())
|
||||
}
|
||||
|
||||
private fun writeDouble(toWrite: Double, byteWidth: ByteWidth) {
|
||||
buffer.requestAdditionalCapacity(byteWidth.value)
|
||||
when (byteWidth.value) {
|
||||
4 -> buffer.put(toWrite.toFloat())
|
||||
8 -> buffer.put(toWrite)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeOffset(toWrite: Int, byteWidth: ByteWidth) {
|
||||
buffer.requestAdditionalCapacity(byteWidth.value)
|
||||
val relativeOffset = (buffer.writePosition - toWrite)
|
||||
if (byteWidth.value != 8 && relativeOffset >= 1L shl byteWidth.value * 8) error("invalid offset $relativeOffset, writer pos ${buffer.writePosition}")
|
||||
writeInt(relativeOffset, byteWidth)
|
||||
}
|
||||
|
||||
private inline fun writeBlob(key: Int, blob: ByteArray, type: FlexBufferType, trailing: Boolean): Value {
|
||||
val bitWidth = blob.size.toULong().widthInUBits()
|
||||
val byteWidth = align(bitWidth)
|
||||
|
||||
writeInt(blob.size, byteWidth)
|
||||
|
||||
val sloc: Int = buffer.writePosition
|
||||
buffer.requestAdditionalCapacity(blob.size + trailing.compareTo(false))
|
||||
buffer.put(blob, 0, blob.size)
|
||||
if (trailing) {
|
||||
buffer.put(ZeroByte)
|
||||
}
|
||||
return Value(type, key, bitWidth, sloc.toULong())
|
||||
}
|
||||
|
||||
private fun writeIntArray(value: IntArray, byteWidth: ByteWidth) =
|
||||
writeIntegerArray(0, value.size, byteWidth) { value[it].toULong() }
|
||||
|
||||
private fun writeIntArray(value: ShortArray, byteWidth: ByteWidth) =
|
||||
writeIntegerArray(0, value.size, byteWidth) { value[it].toULong() }
|
||||
|
||||
private fun writeIntArray(value: LongArray, byteWidth: ByteWidth) =
|
||||
writeIntegerArray(0, value.size, byteWidth) { value[it].toULong() }
|
||||
|
||||
private fun writeFloatArray(value: FloatArray) {
|
||||
buffer.requestAdditionalCapacity(Float.SIZE_BYTES * value.size)
|
||||
value.forEach { buffer.put(it) }
|
||||
}
|
||||
|
||||
private fun writeFloatArray(value: DoubleArray) {
|
||||
buffer.requestAdditionalCapacity(Double.SIZE_BYTES * value.size)
|
||||
value.forEach { buffer.put(it) }
|
||||
}
|
||||
|
||||
private inline fun writeIntegerArray(
|
||||
start: Int,
|
||||
size: Int,
|
||||
byteWidth: ByteWidth,
|
||||
crossinline valueBlock: (Int) -> ULong
|
||||
) {
|
||||
buffer.requestAdditionalCapacity(size * byteWidth.value)
|
||||
return when (byteWidth.value) {
|
||||
1 -> for (i in start until start + size) {
|
||||
buffer.put(valueBlock(i).toUByte())
|
||||
}
|
||||
2 -> for (i in start until start + size) {
|
||||
buffer.put(valueBlock(i).toUShort())
|
||||
}
|
||||
4 -> for (i in start until start + size) {
|
||||
buffer.put(valueBlock(i).toUInt())
|
||||
}
|
||||
8 -> for (i in start until start + size) {
|
||||
buffer.put(valueBlock(i))
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeInt(value: Int, byteWidth: ByteWidth) {
|
||||
buffer.requestAdditionalCapacity(byteWidth.value)
|
||||
when (byteWidth.value) {
|
||||
1 -> buffer.put(value.toUByte())
|
||||
2 -> buffer.put(value.toUShort())
|
||||
4 -> buffer.put(value.toUInt())
|
||||
8 -> buffer.put(value.toULong())
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeInt(value: ULong, byteWidth: ByteWidth) {
|
||||
buffer.requestAdditionalCapacity(byteWidth.value)
|
||||
when(byteWidth.value) {
|
||||
1 -> buffer.put(value.toUByte())
|
||||
2 -> buffer.put(value.toUShort())
|
||||
4 -> buffer.put(value.toUInt())
|
||||
8 -> buffer.put(value)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
// Align to prepare for writing a scalar with a certain size.
|
||||
// returns the amounts of bytes needed to be written.
|
||||
private fun align(alignment: BitWidth): ByteWidth {
|
||||
val byteWidth = 1 shl alignment.value
|
||||
var padBytes = paddingBytes(buffer.writePosition, byteWidth)
|
||||
buffer.requestCapacity(buffer.capacity + padBytes)
|
||||
while (padBytes-- != 0) {
|
||||
buffer.put(ZeroByte)
|
||||
}
|
||||
return ByteWidth(byteWidth)
|
||||
}
|
||||
|
||||
private fun calculateKeyVectorBitWidth(start: Int, length: Int): BitWidth {
|
||||
val bitWidth = length.toULong().widthInUBits()
|
||||
var width = bitWidth
|
||||
val prefixElems = 1
|
||||
// Check bit widths and types for all elements.
|
||||
for (i in start until stack.size) {
|
||||
val elemWidth = elemWidth(T_KEY, W_8, stack[i].key.toLong(), buffer.writePosition, i + prefixElems)
|
||||
width = width.max(elemWidth)
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
private fun createKeyVector(start: Int, length: Int): Value {
|
||||
// Figure out smallest bit width we can store this vector with.
|
||||
val bitWidth = calculateKeyVectorBitWidth(start, length)
|
||||
val byteWidth = align(bitWidth)
|
||||
// Write vector. First the keys width/offset if available, and size.
|
||||
writeInt(length, byteWidth)
|
||||
// Then the actual data.
|
||||
val vloc = buffer.writePosition.toULong()
|
||||
for (i in start until stack.size) {
|
||||
val pos = stack[i].key
|
||||
if (pos == -1) error("invalid position $pos for key")
|
||||
writeOffset(stack[i].key, byteWidth)
|
||||
}
|
||||
// Then the types.
|
||||
return Value(T_VECTOR_KEY, -1, bitWidth, vloc)
|
||||
}
|
||||
|
||||
private inline fun createVector(key: Int, start: Int, length: Int, keys: Value? = null): Value {
|
||||
return createAnyVector(key, start, length, T_VECTOR, keys) {
|
||||
// add types since we are not creating a typed vector.
|
||||
buffer.requestAdditionalCapacity(stack.size)
|
||||
for (i in start until stack.size) {
|
||||
buffer.put(stack[i].storedPackedType(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun putMap(key: Int, start: Int, length: Int, keys: Value? = null): Value {
|
||||
return createAnyVector(key, start, length, T_MAP, keys) {
|
||||
// add types since we are not creating a typed vector.
|
||||
buffer.requestAdditionalCapacity(stack.size)
|
||||
for (i in start until stack.size) {
|
||||
buffer.put(stack[i].storedPackedType(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun createTypedVector(key: Int, start: Int, length: Int, keys: Value? = null): Value {
|
||||
// We assume the callers of this method guarantees all elements are of the same type.
|
||||
val elementType: FlexBufferType = stack[start].type
|
||||
for (i in start + 1 until length) {
|
||||
if (elementType != stack[i].type) error("TypedVector does not support array of different element types")
|
||||
}
|
||||
if (!elementType.isTypedVectorElementType()) error("TypedVector does not support this element type")
|
||||
return createAnyVector(key, start, length, elementType.toTypedVector(), keys)
|
||||
}
|
||||
|
||||
private inline fun createAnyVector(
|
||||
key: Int,
|
||||
start: Int,
|
||||
length: Int,
|
||||
type: FlexBufferType,
|
||||
keys: Value? = null,
|
||||
crossinline typeBlock: (BitWidth) -> Unit = {}
|
||||
): Value {
|
||||
// Figure out the smallest bit width we can store this vector with.
|
||||
var bitWidth = W_8.max(length.toULong().widthInUBits())
|
||||
var prefixElems = 1
|
||||
if (keys != null) {
|
||||
// If this vector is part of a map, we will pre-fix an offset to the keys
|
||||
// to this vector.
|
||||
bitWidth = bitWidth.max(keys.elemWidth(buffer.writePosition, 0))
|
||||
prefixElems += 2
|
||||
}
|
||||
// Check bit widths and types for all elements.
|
||||
for (i in start until stack.size) {
|
||||
val elemWidth = stack[i].elemWidth(buffer.writePosition, i + prefixElems)
|
||||
bitWidth = bitWidth.max(elemWidth)
|
||||
}
|
||||
val byteWidth = align(bitWidth)
|
||||
// Write vector. First the keys width/offset if available, and size.
|
||||
if (keys != null) {
|
||||
writeOffset(keys.iValue.toInt(), byteWidth)
|
||||
writeInt(1 shl keys.minBitWidth.value, byteWidth)
|
||||
}
|
||||
// write the size
|
||||
writeInt(length, byteWidth)
|
||||
|
||||
// Then the actual data.
|
||||
val vloc: Int = buffer.writePosition
|
||||
for (i in start until stack.size) {
|
||||
writeAny(stack[i], byteWidth)
|
||||
}
|
||||
|
||||
// Optionally you can introduce the types for non-typed vector
|
||||
typeBlock(bitWidth)
|
||||
return Value(type, key, bitWidth, vloc.toULong())
|
||||
}
|
||||
|
||||
// A lambda to sort map keys
|
||||
internal val keyComparator = object : Comparator<Value> {
|
||||
override fun compare(a: Value, b: Value): Int {
|
||||
var ia: Int = a.key
|
||||
var io: Int = b.key
|
||||
var c1: Byte
|
||||
var c2: Byte
|
||||
do {
|
||||
c1 = buffer[ia]
|
||||
c2 = buffer[io]
|
||||
if (c1.toInt() == 0) return c1 - c2
|
||||
ia++
|
||||
io++
|
||||
} while (c1 == c2)
|
||||
return c1 - c2
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* No keys or strings will be shared
|
||||
*/
|
||||
public const val SHARE_NONE: Int = 0
|
||||
|
||||
/**
|
||||
* Keys will be shared between elements. Identical keys will only be serialized once, thus possibly saving space.
|
||||
* But serialization performance might be slower and consumes more memory.
|
||||
*/
|
||||
public const val SHARE_KEYS: Int = 1
|
||||
|
||||
/**
|
||||
* Strings will be shared between elements. Identical strings will only be serialized once, thus possibly saving space.
|
||||
* But serialization performance might be slower and consumes more memory. This is ideal if you expect many repeated
|
||||
* strings on the message.
|
||||
*/
|
||||
public const val SHARE_STRINGS: Int = 2
|
||||
|
||||
/**
|
||||
* Strings and keys will be shared between elements.
|
||||
*/
|
||||
public const val SHARE_KEYS_AND_STRINGS: Int = 3
|
||||
}
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
@JvmInline
|
||||
public value class BitWidth(public val value: Int) {
|
||||
public inline fun max(other: BitWidth): BitWidth = if (this.value >= other.value) this else other
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
public value class ByteWidth(public val value: Int)
|
||||
|
||||
@JvmInline
|
||||
public value class FlexBufferType(public val value: Int) {
|
||||
public operator fun minus(other: FlexBufferType): FlexBufferType = FlexBufferType(this.value - other.value)
|
||||
public operator fun plus(other: FlexBufferType): FlexBufferType = FlexBufferType(this.value + other.value)
|
||||
public operator fun compareTo(other: FlexBufferType): Int = this.value - other.value
|
||||
}
|
||||
|
||||
internal operator fun Int.times(width: ByteWidth): Int = this * width.value
|
||||
internal operator fun Int.minus(width: ByteWidth): Int = this - width.value
|
||||
internal operator fun Int.plus(width: ByteWidth): Int = this + width.value
|
||||
internal operator fun Int.minus(type: FlexBufferType): Int = this - type.value
|
||||
|
||||
// Returns a Key string from the buffer starting at index [start]. Key Strings are stored as
|
||||
// C-Strings, ending with '\0'. If zero byte not found returns empty string.
|
||||
internal inline fun ReadBuffer.getKeyString(start: Int): String {
|
||||
val i = findFirst(0.toByte(), start)
|
||||
return if (i >= 0) getString(start, i - start) else ""
|
||||
}
|
||||
|
||||
// read unsigned int with size byteWidth and return as a 64-bit integer
|
||||
internal inline fun ReadBuffer.readULong(end: Int, byteWidth: ByteWidth): ULong {
|
||||
return when (byteWidth.value) {
|
||||
1 -> this.getUByte(end).toULong()
|
||||
2 -> this.getUShort(end).toULong()
|
||||
4 -> this.getUInt(end).toULong()
|
||||
8 -> this.getULong(end)
|
||||
else -> error("invalid byte width $byteWidth for scalar unsigned integer")
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun ReadBuffer.readFloat(end: Int, byteWidth: ByteWidth): Double {
|
||||
return when (byteWidth.value) {
|
||||
4 -> this.getFloat(end).toDouble()
|
||||
8 -> this.getDouble(end)
|
||||
else -> error("invalid byte width $byteWidth for floating point scalar") // we should never reach here
|
||||
}
|
||||
}
|
||||
// return position on the [ReadBuffer] of the element that the offset is pointing to
|
||||
// we assume all offset fits on a int, since ReadBuffer operates with that assumption
|
||||
internal inline fun ReadBuffer.indirect(offset: Int, byteWidth: ByteWidth): Int = offset - readInt(offset, byteWidth)
|
||||
// returns the size of an array-like element from [ReadBuffer].
|
||||
internal inline fun ReadBuffer.readSize(end: Int, byteWidth: ByteWidth) = readInt(end - byteWidth, byteWidth)
|
||||
internal inline fun ReadBuffer.readUInt(end: Int, byteWidth: ByteWidth): UInt = readULong(end, byteWidth).toUInt()
|
||||
internal inline fun ReadBuffer.readInt(end: Int, byteWidth: ByteWidth): Int = readULong(end, byteWidth).toInt()
|
||||
internal inline fun ReadBuffer.readLong(end: Int, byteWidth: ByteWidth): Long = readULong(end, byteWidth).toLong()
|
||||
|
||||
internal fun IntArray.widthInUBits(): BitWidth = arrayWidthInUBits(this.size) { this[it].toULong().widthInUBits() }
|
||||
internal fun ShortArray.widthInUBits(): BitWidth = arrayWidthInUBits(this.size) { this[it].toULong().widthInUBits() }
|
||||
internal fun LongArray.widthInUBits(): BitWidth = arrayWidthInUBits(this.size) { this[it].toULong().widthInUBits() }
|
||||
|
||||
private inline fun arrayWidthInUBits(size: Int, crossinline elemWidthBlock: (Int) -> BitWidth): BitWidth {
|
||||
// Figure out smallest bit width we can store this vector with.
|
||||
var bitWidth = W_8.max(size.toULong().widthInUBits())
|
||||
// Check bit widths and types for all elements.
|
||||
for (i in 0 until size) {
|
||||
// since we know its inline types we can just assume elmentWidth to be the value width in bits.
|
||||
bitWidth = bitWidth.max(elemWidthBlock(i))
|
||||
}
|
||||
return bitWidth
|
||||
}
|
||||
|
||||
internal fun ULong.widthInUBits(): BitWidth = when {
|
||||
this <= MAX_UBYTE_ULONG -> W_8
|
||||
this <= UShort.MAX_VALUE -> W_16
|
||||
this <= UInt.MAX_VALUE -> W_32
|
||||
else -> W_64
|
||||
}
|
||||
|
||||
// returns the number of bytes needed for padding the scalar of size scalarSize.
|
||||
internal inline fun paddingBytes(bufSize: Int, scalarSize: Int): Int = bufSize.inv() + 1 and scalarSize - 1
|
||||
|
||||
internal inline fun FlexBufferType.isInline(): Boolean = this.value <= T_FLOAT.value || this == T_BOOL
|
||||
|
||||
internal fun FlexBufferType.isScalar(): Boolean = when (this) {
|
||||
T_INT, T_UINT, T_FLOAT, T_BOOL -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
internal fun FlexBufferType.isIndirectScalar(): Boolean = when (this) {
|
||||
T_INDIRECT_INT, T_INDIRECT_UINT, T_INDIRECT_FLOAT -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
internal fun FlexBufferType.isTypedVector(): Boolean =
|
||||
this >= T_VECTOR_INT && this <= T_VECTOR_STRING_DEPRECATED || this == T_VECTOR_BOOL
|
||||
|
||||
internal fun FlexBufferType.isTypedVectorElementType(): Boolean =
|
||||
(this.value in T_INT.value..T_KEY.value) || this == T_BOOL
|
||||
|
||||
// returns the typed vector of a given scalar type.
|
||||
internal fun FlexBufferType.toTypedVector(): FlexBufferType = (this - T_INT) + T_VECTOR_INT
|
||||
// returns the element type of given typed vector.
|
||||
internal fun FlexBufferType.toElementTypedVector(): FlexBufferType = this - T_VECTOR_INT + T_INT
|
||||
|
||||
// Holds information about the elements inserted on the buffer.
|
||||
internal data class Value(
|
||||
var type: FlexBufferType = T_INT,
|
||||
var key: Int = -1,
|
||||
var minBitWidth: BitWidth = W_8,
|
||||
var iValue: ULong = 0UL, // integer value
|
||||
var dValue: Double = 0.0 // TODO(paulovap): maybe we can keep floating type on iValue as well.
|
||||
) { // float value
|
||||
|
||||
inline fun storedPackedType(parentBitWidth: BitWidth = W_8): Byte = packedType(storedWidth(parentBitWidth), type)
|
||||
|
||||
private inline fun packedType(bitWidth: BitWidth, type: FlexBufferType): Byte =
|
||||
(bitWidth.value or (type.value shl 2)).toByte()
|
||||
|
||||
private inline fun storedWidth(parentBitWidth: BitWidth): BitWidth =
|
||||
if (type.isInline()) minBitWidth.max(parentBitWidth) else minBitWidth
|
||||
|
||||
fun elemWidth(bufSize: Int, elemIndex: Int): BitWidth =
|
||||
elemWidth(type, minBitWidth, iValue.toLong(), bufSize, elemIndex)
|
||||
}
|
||||
|
||||
internal fun elemWidth(
|
||||
type: FlexBufferType,
|
||||
minBitWidth: BitWidth,
|
||||
iValue: Long,
|
||||
bufSize: Int,
|
||||
elemIndex: Int
|
||||
): BitWidth {
|
||||
if (type.isInline()) return minBitWidth
|
||||
|
||||
// We have an absolute offset, but want to store a relative offset
|
||||
// elem_index elements beyond the current buffer end. Since whether
|
||||
// the relative offset fits in a certain byte_width depends on
|
||||
// the size of the elements before it (and their alignment), we have
|
||||
// to test for each size in turn.
|
||||
// Original implementation checks for largest scalar
|
||||
// which is long unsigned int
|
||||
var byteWidth = 1
|
||||
while (byteWidth <= 32) {
|
||||
// Where are we going to write this offset?
|
||||
val offsetLoc: Int = bufSize + paddingBytes(bufSize, byteWidth) + elemIndex * byteWidth
|
||||
// Compute relative offset.
|
||||
val offset: Int = offsetLoc - iValue.toInt()
|
||||
// Does it fit?
|
||||
val bitWidth = offset.toULong().widthInUBits()
|
||||
if (1 shl bitWidth.value == byteWidth) return bitWidth
|
||||
byteWidth *= 2
|
||||
}
|
||||
return W_64
|
||||
}
|
||||
|
||||
// For debugging purposes, convert type to a human-readable string.
|
||||
internal fun FlexBufferType.typeToString(): String = when (this) {
|
||||
T_NULL -> "Null"
|
||||
T_INT -> "Int"
|
||||
T_UINT -> "UInt"
|
||||
T_FLOAT -> "Float"
|
||||
T_KEY -> "Key"
|
||||
T_STRING -> "String"
|
||||
T_INDIRECT_INT -> "IndirectInt"
|
||||
T_INDIRECT_UINT -> "IndirectUInt"
|
||||
T_INDIRECT_FLOAT -> "IndirectFloat"
|
||||
T_MAP -> "Map"
|
||||
T_VECTOR -> "Vector"
|
||||
T_VECTOR_INT -> "IntVector"
|
||||
T_VECTOR_UINT -> "UIntVector"
|
||||
T_VECTOR_FLOAT -> "FloatVector"
|
||||
T_VECTOR_KEY -> "KeyVector"
|
||||
T_VECTOR_STRING_DEPRECATED -> "StringVectorDeprecated"
|
||||
T_VECTOR_INT2 -> "Int2Vector"
|
||||
T_VECTOR_UINT2 -> "UInt2Vector"
|
||||
T_VECTOR_FLOAT2 -> "Float2Vector"
|
||||
T_VECTOR_INT3 -> "Int3Vector"
|
||||
T_VECTOR_UINT3 -> "UInt3Vector"
|
||||
T_VECTOR_FLOAT3 -> "Float3Vector"
|
||||
T_VECTOR_INT4 -> "Int4Vector"
|
||||
T_VECTOR_UINT4 -> "UInt4Vector"
|
||||
T_VECTOR_FLOAT4 -> "Float4Vector"
|
||||
T_BLOB -> "BlobVector"
|
||||
T_BOOL -> "BoolVector"
|
||||
T_VECTOR_BOOL -> "BoolVector"
|
||||
else -> "UnknownType"
|
||||
}
|
||||
|
||||
// Few repeated values used in hot path is cached here
|
||||
internal fun emptyBlob() = Blob(emptyBuffer, 1, ByteWidth(1))
|
||||
internal fun emptyVector() = Vector(emptyBuffer, 1, ByteWidth(1))
|
||||
internal fun emptyMap() = Map(ArrayReadWriteBuffer(3), 3, ByteWidth(1))
|
||||
internal fun nullReference() = Reference(emptyBuffer, 1, ByteWidth(0), T_NULL.value)
|
||||
internal fun nullKey() = Key(emptyBuffer, 1)
|
||||
|
||||
internal const val ZeroByte = 0.toByte()
|
||||
internal const val MAX_UBYTE_ULONG = 255UL
|
||||
internal const val MAX_UBYTE = 255
|
||||
internal const val MAX_USHORT = 65535
|
||||
|
||||
// value bit width possible sizes
|
||||
internal val W_8 = BitWidth(0)
|
||||
internal val W_16 = BitWidth(1)
|
||||
internal val W_32 = BitWidth(2)
|
||||
internal val W_64 = BitWidth(3)
|
||||
|
||||
// These are used as the upper 6 bits of a type field to indicate the actual type.
|
||||
internal val T_INVALID = FlexBufferType(-1)
|
||||
internal val T_NULL = FlexBufferType(0)
|
||||
internal val T_INT = FlexBufferType(1)
|
||||
internal val T_UINT = FlexBufferType(2)
|
||||
internal val T_FLOAT = FlexBufferType(3) // Types above stored inline, types below are stored in an offset.
|
||||
internal val T_KEY = FlexBufferType(4)
|
||||
internal val T_STRING = FlexBufferType(5)
|
||||
internal val T_INDIRECT_INT = FlexBufferType(6)
|
||||
internal val T_INDIRECT_UINT = FlexBufferType(7)
|
||||
internal val T_INDIRECT_FLOAT = FlexBufferType(8)
|
||||
internal val T_MAP = FlexBufferType(9)
|
||||
internal val T_VECTOR = FlexBufferType(10) // Untyped.
|
||||
internal val T_VECTOR_INT = FlexBufferType(11) // Typed any size = stores no type table).
|
||||
internal val T_VECTOR_UINT = FlexBufferType(12)
|
||||
internal val T_VECTOR_FLOAT = FlexBufferType(13)
|
||||
internal val T_VECTOR_KEY = FlexBufferType(14)
|
||||
// DEPRECATED, use FBT_VECTOR or FBT_VECTOR_KEY instead.
|
||||
// more info on https://github.com/google/flatbuffers/issues/5627.
|
||||
internal val T_VECTOR_STRING_DEPRECATED = FlexBufferType(15)
|
||||
internal val T_VECTOR_INT2 = FlexBufferType(16) // Typed tuple = no type table; no size field).
|
||||
internal val T_VECTOR_UINT2 = FlexBufferType(17)
|
||||
internal val T_VECTOR_FLOAT2 = FlexBufferType(18)
|
||||
internal val T_VECTOR_INT3 = FlexBufferType(19) // Typed triple = no type table; no size field).
|
||||
internal val T_VECTOR_UINT3 = FlexBufferType(20)
|
||||
internal val T_VECTOR_FLOAT3 = FlexBufferType(21)
|
||||
internal val T_VECTOR_INT4 = FlexBufferType(22) // Typed quad = no type table; no size field).
|
||||
internal val T_VECTOR_UINT4 = FlexBufferType(23)
|
||||
internal val T_VECTOR_FLOAT4 = FlexBufferType(24)
|
||||
internal val T_BLOB = FlexBufferType(25)
|
||||
internal val T_BOOL = FlexBufferType(26)
|
||||
internal val T_VECTOR_BOOL = FlexBufferType(36) // To Allow the same type of conversion of type to vector type
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
public object Utf8 {
|
||||
/**
|
||||
* Returns the number of bytes in the UTF-8-encoded form of `sequence`. For a string,
|
||||
* this method is equivalent to `string.getBytes(UTF_8).length`, but is more efficient in
|
||||
* both time and space.
|
||||
*
|
||||
* @throws IllegalArgumentException if `sequence` contains ill-formed UTF-16 (unpaired
|
||||
* surrogates)
|
||||
*/
|
||||
private fun computeEncodedLength(sequence: CharSequence): Int {
|
||||
// Warning to maintainers: this implementation is highly optimized.
|
||||
val utf16Length = sequence.length
|
||||
var utf8Length = utf16Length
|
||||
var i = 0
|
||||
|
||||
// This loop optimizes for pure ASCII.
|
||||
while (i < utf16Length && sequence[i].code < 0x80) {
|
||||
i++
|
||||
}
|
||||
|
||||
// This loop optimizes for chars less than 0x800.
|
||||
while (i < utf16Length) {
|
||||
val c = sequence[i]
|
||||
if (c.code < 0x800) {
|
||||
utf8Length += 0x7f - c.code ushr 31 // branch free!
|
||||
} else {
|
||||
utf8Length += encodedLengthGeneral(sequence, i)
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (utf8Length < utf16Length) {
|
||||
// Necessary and sufficient condition for overflow because of maximum 3x expansion
|
||||
error("UTF-8 length does not fit in int: ${(utf8Length + (1L shl 32))}")
|
||||
}
|
||||
return utf8Length
|
||||
}
|
||||
|
||||
private fun encodedLengthGeneral(sequence: CharSequence, start: Int): Int {
|
||||
val utf16Length = sequence.length
|
||||
var utf8Length = 0
|
||||
var i = start
|
||||
while (i < utf16Length) {
|
||||
val c = sequence[i]
|
||||
if (c.code < 0x800) {
|
||||
utf8Length += 0x7f - c.code ushr 31 // branch free!
|
||||
} else {
|
||||
utf8Length += 2
|
||||
if (c.isSurrogate()) {
|
||||
// Check that we have a well-formed surrogate pair.
|
||||
val cp: Int = codePointAt(sequence, i)
|
||||
if (cp < MIN_SUPPLEMENTARY_CODE_POINT) {
|
||||
errorSurrogate(i, utf16Length)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return utf8Length
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the UTF-8-encoded form of `sequence`. For a string,
|
||||
* this method is equivalent to `string.getBytes(UTF_8).length`, but is more efficient in
|
||||
* both time and space.
|
||||
*
|
||||
* @throws IllegalArgumentException if `sequence` contains ill-formed UTF-16 (unpaired
|
||||
* surrogates)
|
||||
*/
|
||||
public fun encodedLength(sequence: CharSequence): Int = computeEncodedLength(sequence)
|
||||
|
||||
/**
|
||||
* Returns whether this is a single-byte codepoint (i.e., ASCII) with the form '0XXXXXXX'.
|
||||
*/
|
||||
public inline fun isOneByte(b: Byte): Boolean = b >= 0
|
||||
|
||||
/**
|
||||
* Returns whether this is a two-byte codepoint with the form 110xxxxx 0xC0..0xDF.
|
||||
*/
|
||||
public inline fun isTwoBytes(b: Byte): Boolean = b < 0xE0.toByte()
|
||||
|
||||
/**
|
||||
* Returns whether this is a three-byte codepoint with the form 1110xxxx 0xE0..0xEF.
|
||||
*/
|
||||
public inline fun isThreeBytes(b: Byte): Boolean = b < 0xF0.toByte()
|
||||
|
||||
/**
|
||||
* Returns whether this is a four-byte codepoint with the form 11110xxx 0xF0..0xF4.
|
||||
*/
|
||||
public inline fun isFourByte(b: Byte): Boolean = b < 0xF8.toByte()
|
||||
|
||||
public fun handleOneByte(byte1: Byte, resultArr: CharArray, resultPos: Int) {
|
||||
resultArr[resultPos] = byte1.toInt().toChar()
|
||||
}
|
||||
|
||||
public fun handleTwoBytes(
|
||||
byte1: Byte,
|
||||
byte2: Byte,
|
||||
resultArr: CharArray,
|
||||
resultPos: Int
|
||||
) {
|
||||
// Simultaneously checks for illegal trailing-byte in leading position (<= '11000000') and
|
||||
// overlong 2-byte, '11000001'.
|
||||
if (byte1 < 0xC2.toByte()) {
|
||||
error("Invalid UTF-8: Illegal leading byte in 2 bytes utf")
|
||||
}
|
||||
if (isNotTrailingByte(byte2)) {
|
||||
error("Invalid UTF-8: Illegal trailing byte in 2 bytes utf")
|
||||
}
|
||||
resultArr[resultPos] = (byte1.toInt() and 0x1F shl 6 or trailingByteValue(byte2)).toChar()
|
||||
}
|
||||
|
||||
public fun handleThreeBytes(
|
||||
byte1: Byte,
|
||||
byte2: Byte,
|
||||
byte3: Byte,
|
||||
resultArr: CharArray,
|
||||
resultPos: Int
|
||||
) {
|
||||
if (isNotTrailingByte(byte2) || // overlong? 5 most significant bits must not all be zero
|
||||
byte1 == 0xE0.toByte() && byte2 < 0xA0.toByte() || // check for illegal surrogate codepoints
|
||||
byte1 == 0xED.toByte() && byte2 >= 0xA0.toByte() ||
|
||||
isNotTrailingByte(byte3)
|
||||
) {
|
||||
error("Invalid UTF-8")
|
||||
}
|
||||
resultArr[resultPos] =
|
||||
(byte1.toInt() and 0x0F shl 12 or (trailingByteValue(byte2) shl 6) or trailingByteValue(byte3)).toChar()
|
||||
}
|
||||
|
||||
public fun handleFourBytes(
|
||||
byte1: Byte,
|
||||
byte2: Byte,
|
||||
byte3: Byte,
|
||||
byte4: Byte,
|
||||
resultArr: CharArray,
|
||||
resultPos: Int
|
||||
) {
|
||||
if (isNotTrailingByte(byte2) || // Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// valid 4-byte leading byte?
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// overlong? 4 most significant bits must not all be zero
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// codepoint larger than the highest code point (U+10FFFF)?
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(byte1.toInt() shl 28) + (byte2 - 0x90.toByte()) shr 30 != 0 || isNotTrailingByte(byte3) ||
|
||||
isNotTrailingByte(byte4)
|
||||
) {
|
||||
error("Invalid UTF-8")
|
||||
}
|
||||
val codepoint: Int = (
|
||||
byte1.toInt() and 0x07 shl 18
|
||||
or (trailingByteValue(byte2) shl 12)
|
||||
or (trailingByteValue(byte3) shl 6)
|
||||
or trailingByteValue(byte4)
|
||||
)
|
||||
resultArr[resultPos] = highSurrogate(codepoint)
|
||||
resultArr[resultPos + 1] = lowSurrogate(codepoint)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the byte is not a valid continuation of the form '10XXXXXX'.
|
||||
*/
|
||||
private fun isNotTrailingByte(b: Byte): Boolean = b > 0xBF.toByte()
|
||||
|
||||
/**
|
||||
* Returns the actual value of the trailing byte (removes the prefix '10') for composition.
|
||||
*/
|
||||
private fun trailingByteValue(b: Byte): Int = b.toInt() and 0x3F
|
||||
|
||||
private fun highSurrogate(codePoint: Int): Char =
|
||||
(
|
||||
Char.MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT ushr 10) +
|
||||
(codePoint ushr 10)
|
||||
)
|
||||
|
||||
private fun lowSurrogate(codePoint: Int): Char = (Char.MIN_LOW_SURROGATE + (codePoint and 0x3ff))
|
||||
|
||||
/**
|
||||
* Encode a [CharSequence] UTF8 codepoint into a byte array.
|
||||
* @param `in` CharSequence to be encoded
|
||||
* @param start start position of the first char in the codepoint
|
||||
* @param out byte array of 4 bytes to be filled
|
||||
* @return return the amount of bytes occupied by the codepoint
|
||||
*/
|
||||
public fun encodeUtf8CodePoint(input: CharSequence, start: Int, out: ByteArray): Int {
|
||||
// utf8 codepoint needs at least 4 bytes
|
||||
val inLength = input.length
|
||||
if (start >= inLength) {
|
||||
return 0
|
||||
}
|
||||
val c = input[start]
|
||||
return if (c.code < 0x80) {
|
||||
// One byte (0xxx xxxx)
|
||||
out[0] = c.code.toByte()
|
||||
1
|
||||
} else if (c.code < 0x800) {
|
||||
// Two bytes (110x xxxx 10xx xxxx)
|
||||
out[0] = (0xC0 or (c.code ushr 6)).toByte()
|
||||
out[1] = (0x80 or (0x3F and c.code)).toByte()
|
||||
2
|
||||
} else if (c < Char.MIN_SURROGATE || Char.MAX_SURROGATE < c) {
|
||||
// Three bytes (1110 xxxx 10xx xxxx 10xx xxxx)
|
||||
// Maximum single-char code point is 0xFFFF, 16 bits.
|
||||
out[0] = (0xE0 or (c.code ushr 12)).toByte()
|
||||
out[1] = (0x80 or (0x3F and (c.code ushr 6))).toByte()
|
||||
out[2] = (0x80 or (0x3F and c.code)).toByte()
|
||||
3
|
||||
} else {
|
||||
// Four bytes (1111 xxxx 10xx xxxx 10xx xxxx 10xx xxxx)
|
||||
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
|
||||
// bytes
|
||||
val low: Char = input[start + 1]
|
||||
if (start + 1 == inLength || !(c.isHighSurrogate() and low.isLowSurrogate())) {
|
||||
errorSurrogate(start, inLength)
|
||||
}
|
||||
val codePoint: Int = toCodePoint(c, low)
|
||||
out[0] = (0xF shl 4 or (codePoint ushr 18)).toByte()
|
||||
out[1] = (0x80 or (0x3F and (codePoint ushr 12))).toByte()
|
||||
out[2] = (0x80 or (0x3F and (codePoint ushr 6))).toByte()
|
||||
out[3] = (0x80 or (0x3F and codePoint)).toByte()
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes a code point starting at index into out. Out parameter
|
||||
// should have at least 2 chars.
|
||||
public fun decodeUtf8CodePoint(bytes: ReadBuffer, index: Int, out: CharArray) {
|
||||
// Bitwise OR combines the sign bits so any negative value fails the check.
|
||||
val b1 = bytes[index]
|
||||
when {
|
||||
isOneByte(b1) -> handleOneByte(b1, out, 0)
|
||||
isTwoBytes(b1) -> handleTwoBytes(b1, bytes[index + 1], out, 0)
|
||||
isThreeBytes(b1) -> handleThreeBytes(b1, bytes[index + 1], bytes[index + 2], out, 0)
|
||||
else -> handleFourBytes(b1, bytes[index + 1], bytes[index + 2], bytes[index + 3], out, 0)
|
||||
}
|
||||
}
|
||||
|
||||
public fun decodeUtf8Array(bytes: ByteArray, index: Int = 0, size: Int = bytes.size): String {
|
||||
// Bitwise OR combines the sign bits so any negative value fails the check.
|
||||
if (index or size or bytes.size - index - size < 0) {
|
||||
error("buffer length=${bytes.size}, index=$index, size=$size")
|
||||
}
|
||||
var offset = index
|
||||
val limit = offset + size
|
||||
|
||||
// The longest possible resulting String is the same as the number of input bytes, when it is
|
||||
// all ASCII. For other cases, this over-allocates and we will truncate in the end.
|
||||
val resultArr = CharArray(size)
|
||||
var resultPos = 0
|
||||
|
||||
// Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
|
||||
// This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
|
||||
while (offset < limit) {
|
||||
val b = bytes[offset]
|
||||
if (!isOneByte(b)) {
|
||||
break
|
||||
}
|
||||
offset++
|
||||
handleOneByte(b, resultArr, resultPos++)
|
||||
}
|
||||
while (offset < limit) {
|
||||
val byte1 = bytes[offset++]
|
||||
if (isOneByte(byte1)) {
|
||||
handleOneByte(byte1, resultArr, resultPos++)
|
||||
// It's common for there to be multiple ASCII characters in a run mixed in, so add an
|
||||
// extra optimized loop to take care of these runs.
|
||||
while (offset < limit) {
|
||||
val b = bytes[offset]
|
||||
if (!isOneByte(b)) {
|
||||
break
|
||||
}
|
||||
offset++
|
||||
handleOneByte(b, resultArr, resultPos++)
|
||||
}
|
||||
} else if (isTwoBytes(byte1)) {
|
||||
if (offset >= limit) {
|
||||
error("Invalid UTF-8")
|
||||
}
|
||||
handleTwoBytes(
|
||||
byte1, /* byte2 */
|
||||
bytes[offset++], resultArr, resultPos++
|
||||
)
|
||||
} else if (isThreeBytes(byte1)) {
|
||||
if (offset >= limit - 1) {
|
||||
error("Invalid UTF-8")
|
||||
}
|
||||
handleThreeBytes(
|
||||
byte1, /* byte2 */
|
||||
bytes[offset++], /* byte3 */
|
||||
bytes[offset++],
|
||||
resultArr,
|
||||
resultPos++
|
||||
)
|
||||
} else {
|
||||
if (offset >= limit - 2) {
|
||||
error("Invalid UTF-8")
|
||||
}
|
||||
handleFourBytes(
|
||||
byte1, /* byte2 */
|
||||
bytes[offset++], /* byte3 */
|
||||
bytes[offset++], /* byte4 */
|
||||
bytes[offset++],
|
||||
resultArr,
|
||||
resultPos++
|
||||
)
|
||||
// 4-byte case requires two chars.
|
||||
resultPos++
|
||||
}
|
||||
}
|
||||
return resultArr.concatToString(0, resultPos)
|
||||
}
|
||||
|
||||
public fun encodeUtf8Array(input: CharSequence,
|
||||
out: ByteArray,
|
||||
offset: Int = 0,
|
||||
length: Int = out.size - offset): Int {
|
||||
val utf16Length = input.length
|
||||
var j = offset
|
||||
var i = 0
|
||||
val limit = offset + length
|
||||
// Designed to take advantage of
|
||||
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
|
||||
|
||||
if (utf16Length == 0)
|
||||
return 0
|
||||
var cc: Char = input[i]
|
||||
while (i < utf16Length && i + j < limit && input[i].also { cc = it }.code < 0x80) {
|
||||
out[j + i] = cc.code.toByte()
|
||||
i++
|
||||
}
|
||||
if (i == utf16Length) {
|
||||
return j + utf16Length
|
||||
}
|
||||
j += i
|
||||
var c: Char
|
||||
while (i < utf16Length) {
|
||||
c = input[i]
|
||||
if (c.code < 0x80 && j < limit) {
|
||||
out[j++] = c.code.toByte()
|
||||
} else if (c.code < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
|
||||
out[j++] = (0xF shl 6 or (c.code ushr 6)).toByte()
|
||||
out[j++] = (0x80 or (0x3F and c.code)).toByte()
|
||||
} else if ((c < Char.MIN_SURROGATE || Char.MAX_SURROGATE < c) && j <= limit - 3) {
|
||||
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
|
||||
out[j++] = (0xF shl 5 or (c.code ushr 12)).toByte()
|
||||
out[j++] = (0x80 or (0x3F and (c.code ushr 6))).toByte()
|
||||
out[j++] = (0x80 or (0x3F and c.code)).toByte()
|
||||
} else if (j <= limit - 4) {
|
||||
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
|
||||
// four UTF-8 bytes
|
||||
var low: Char = Char.MIN_VALUE
|
||||
if (i + 1 == input.length ||
|
||||
!isSurrogatePair(c, input[++i].also { low = it })
|
||||
) {
|
||||
errorSurrogate(i - 1, utf16Length)
|
||||
}
|
||||
val codePoint: Int = toCodePoint(c, low)
|
||||
out[j++] = (0xF shl 4 or (codePoint ushr 18)).toByte()
|
||||
out[j++] = (0x80 or (0x3F and (codePoint ushr 12))).toByte()
|
||||
out[j++] = (0x80 or (0x3F and (codePoint ushr 6))).toByte()
|
||||
out[j++] = (0x80 or (0x3F and codePoint)).toByte()
|
||||
} else {
|
||||
// If we are surrogates and we're not a surrogate pair, always throw an
|
||||
// UnpairedSurrogateException instead of an ArrayOutOfBoundsException.
|
||||
if (Char.MIN_SURROGATE <= c && c <= Char.MAX_SURROGATE &&
|
||||
(i + 1 == input.length || !isSurrogatePair(c, input[i + 1]))
|
||||
) {
|
||||
errorSurrogate(i, utf16Length)
|
||||
}
|
||||
error("Failed writing character ${c.code.toShort().toString(radix = 16)} at index $j")
|
||||
}
|
||||
i++
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
public fun codePointAt(seq: CharSequence, position: Int): Int {
|
||||
var index = position
|
||||
val c1 = seq[index]
|
||||
if (c1.isHighSurrogate() && ++index < seq.length) {
|
||||
val c2 = seq[index]
|
||||
if (c2.isLowSurrogate()) {
|
||||
return toCodePoint(c1, c2)
|
||||
}
|
||||
}
|
||||
return c1.code
|
||||
}
|
||||
|
||||
private fun isSurrogatePair(high: Char, low: Char) = high.isHighSurrogate() and low.isLowSurrogate()
|
||||
|
||||
private fun toCodePoint(high: Char, low: Char): Int = (high.code shl 10) + low.code +
|
||||
(MIN_SUPPLEMENTARY_CODE_POINT - (Char.MIN_HIGH_SURROGATE.code shl 10) - Char.MIN_LOW_SURROGATE.code)
|
||||
|
||||
private fun errorSurrogate(i: Int, utf16Length: Int): Unit =
|
||||
error("Unpaired surrogate at index $i of $utf16Length length")
|
||||
|
||||
// The minimum value of Unicode supplementary code point, constant `U+10000`.
|
||||
private const val MIN_SUPPLEMENTARY_CODE_POINT = 0x010000
|
||||
}
|
||||
+832
@@ -0,0 +1,832 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import com.google.flatbuffers.kotlin.FlexBuffersBuilder.Companion.SHARE_KEYS_AND_STRINGS
|
||||
import kotlin.experimental.and
|
||||
import kotlin.jvm.JvmInline
|
||||
import kotlin.math.pow
|
||||
|
||||
/**
|
||||
* Returns a minified version of this FlexBuffer as a JSON.
|
||||
*/
|
||||
public fun Reference.toJson(): String = ArrayReadWriteBuffer(1024).let {
|
||||
toJson(it)
|
||||
val data = it.data() // it.getString(0, it.writePosition)
|
||||
return data.decodeToString(0, it.writePosition)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a minified version of this FlexBuffer as a JSON.
|
||||
* @param out [ReadWriteBuffer] the JSON will be written.
|
||||
*/
|
||||
public fun Reference.toJson(out: ReadWriteBuffer) {
|
||||
when (type) {
|
||||
T_STRING -> {
|
||||
val start = buffer.indirect(end, parentWidth)
|
||||
val size = buffer.readULong(start - byteWidth, byteWidth).toInt()
|
||||
out.jsonEscape(buffer, start, size)
|
||||
}
|
||||
T_KEY -> {
|
||||
val start = buffer.indirect(end, parentWidth)
|
||||
val end = buffer.findFirst(0.toByte(), start)
|
||||
out.jsonEscape(buffer, start, end - start)
|
||||
}
|
||||
T_BLOB -> {
|
||||
val blob = toBlob()
|
||||
out.jsonEscape(out, blob.end, blob.size)
|
||||
}
|
||||
T_INT -> out.put(toLong().toString())
|
||||
T_UINT -> out.put(toULong().toString())
|
||||
T_FLOAT -> out.put(toDouble().toString())
|
||||
T_NULL -> out.put("null")
|
||||
T_BOOL -> out.put(toBoolean().toString())
|
||||
T_MAP -> toMap().toJson(out)
|
||||
T_VECTOR, T_VECTOR_BOOL, T_VECTOR_FLOAT, T_VECTOR_INT,
|
||||
T_VECTOR_UINT, T_VECTOR_KEY, T_VECTOR_STRING_DEPRECATED -> toVector().toJson(out)
|
||||
else -> error("Unable to convert type ${type.typeToString()} to JSON")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a minified version of this FlexBuffer as a JSON.
|
||||
*/
|
||||
public fun Map.toJson(): String = ArrayReadWriteBuffer(1024).let { toJson(it); it.toString() }
|
||||
|
||||
/**
|
||||
* Returns a minified version of this FlexBuffer as a JSON.
|
||||
* @param out [ReadWriteBuffer] the JSON will be written.
|
||||
*/
|
||||
public fun Map.toJson(out: ReadWriteBuffer) {
|
||||
out.put('{'.code.toByte())
|
||||
// key values pairs
|
||||
for (i in 0 until size) {
|
||||
val key = keyAt(i)
|
||||
out.jsonEscape(buffer, key.start, key.sizeInBytes)
|
||||
out.put(':'.code.toByte())
|
||||
get(i).toJson(out)
|
||||
if (i != size - 1) {
|
||||
out.put(','.code.toByte())
|
||||
}
|
||||
}
|
||||
// close bracket
|
||||
out.put('}'.code.toByte())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a minified version of this FlexBuffer as a JSON.
|
||||
*/
|
||||
public fun Vector.toJson(): String = ArrayReadWriteBuffer(1024).let { toJson(it); it.toString() }
|
||||
|
||||
/**
|
||||
* Returns a minified version of this FlexBuffer as a JSON.
|
||||
* @param out that the JSON is being concatenated.
|
||||
*/
|
||||
public fun Vector.toJson(out: ReadWriteBuffer) {
|
||||
out.put('['.code.toByte())
|
||||
for (i in indices) {
|
||||
get(i).toJson(out)
|
||||
if (i != size - 1) {
|
||||
out.put(','.code.toByte())
|
||||
}
|
||||
}
|
||||
out.put(']'.code.toByte())
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONParser class is used to parse a JSON as FlexBuffers. Calling [JSONParser.parse] fiils [output]
|
||||
* and returns a [Reference] ready to be used.
|
||||
*/
|
||||
@ExperimentalUnsignedTypes
|
||||
public class JSONParser(public var output: FlexBuffersBuilder = FlexBuffersBuilder(1024, SHARE_KEYS_AND_STRINGS)) {
|
||||
private var readPos = 0
|
||||
private var scopes = ScopeStack()
|
||||
|
||||
/**
|
||||
* Parse a json as [String] and returns a [Reference] to a FlexBuffer.
|
||||
*/
|
||||
public fun parse(data: String): Reference = parse(ArrayReadBuffer(data.encodeToByteArray()))
|
||||
|
||||
/**
|
||||
* Parse a json as [ByteArray] and returns a [Reference] to a FlexBuffer.
|
||||
*/
|
||||
public fun parse(data: ByteArray): Reference = parse(ArrayReadBuffer(data))
|
||||
|
||||
/**
|
||||
* Parse a json as [ReadBuffer] and returns a [Reference] to a FlexBuffer.
|
||||
*/
|
||||
public fun parse(data: ReadBuffer): Reference {
|
||||
reset()
|
||||
parseValue(data, nextToken(data), null)
|
||||
if (readPos < data.limit) {
|
||||
val tok = skipWhitespace(data)
|
||||
if (tok != CHAR_EOF) {
|
||||
makeError(data, "Extraneous charaters after parse has finished", tok)
|
||||
}
|
||||
}
|
||||
output.finish()
|
||||
return getRoot(output.buffer)
|
||||
}
|
||||
|
||||
private fun parseValue(data: ReadBuffer, token: Token, key: String? = null): FlexBufferType {
|
||||
return when (token) {
|
||||
TOK_BEGIN_OBJECT -> parseObject(data, key)
|
||||
TOK_BEGIN_ARRAY -> parseArray(data, key)
|
||||
TOK_TRUE -> T_BOOL.also { output[key] = true }
|
||||
TOK_FALSE -> T_BOOL.also { output[key] = false }
|
||||
TOK_NULL -> T_NULL.also { output.putNull(key) }
|
||||
TOK_BEGIN_QUOTE -> parseString(data, key)
|
||||
TOK_NUMBER -> parseNumber(data, data.data(), key)
|
||||
else -> makeError(data, "Unexpected Character while parsing", 'x'.code.toByte())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseObject(data: ReadBuffer, key: String? = null): FlexBufferType {
|
||||
this.scopes.push(SCOPE_OBJ_EMPTY)
|
||||
|
||||
val fPos = output.startMap()
|
||||
val limit = data.limit
|
||||
while (readPos <= limit) {
|
||||
when (val tok = nextToken(data)) {
|
||||
TOK_END_OBJECT -> {
|
||||
this.scopes.pop()
|
||||
output.endMap(fPos, key); return T_MAP
|
||||
}
|
||||
TOK_BEGIN_QUOTE -> {
|
||||
val childKey = readString(data)
|
||||
parseValue(data, nextToken(data), childKey)
|
||||
}
|
||||
else -> makeError(data, "Expecting start of object key", tok)
|
||||
}
|
||||
}
|
||||
makeError(data, "Unable to parse the object", "x".toByte())
|
||||
}
|
||||
|
||||
private fun parseArray(data: ReadBuffer, key: String? = null): FlexBufferType {
|
||||
this.scopes.push(SCOPE_ARRAY_EMPTY)
|
||||
val fPos = output.startVector()
|
||||
var elementType = T_INVALID
|
||||
var multiType = false
|
||||
val limit = data.limit
|
||||
|
||||
while (readPos <= limit) {
|
||||
when (val tok = nextToken(data)) {
|
||||
TOK_END_ARRAY -> {
|
||||
this.scopes.pop()
|
||||
return if (!multiType && elementType.isScalar()) {
|
||||
output.endTypedVector(fPos, key)
|
||||
elementType.toElementTypedVector()
|
||||
} else {
|
||||
output.endVector(key, fPos)
|
||||
T_VECTOR
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val newType = parseValue(data, tok, null)
|
||||
|
||||
if (elementType == T_INVALID) {
|
||||
elementType = newType
|
||||
} else if (newType != elementType) {
|
||||
multiType = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
makeError(data, "Unable to parse the array")
|
||||
}
|
||||
|
||||
private fun parseNumber(data: ReadBuffer, array: ByteArray, key: String?): FlexBufferType {
|
||||
val ary = array
|
||||
var cursor = readPos
|
||||
var c = data[readPos++]
|
||||
var useDouble = false
|
||||
val limit = ary.size
|
||||
var sign = 1
|
||||
var double: Double
|
||||
var long = 0L
|
||||
var digits = 0
|
||||
|
||||
if (c == CHAR_MINUS) {
|
||||
cursor++
|
||||
checkEOF(data, cursor)
|
||||
c = ary[cursor]
|
||||
sign = -1
|
||||
}
|
||||
|
||||
// peek first byte
|
||||
when (c) {
|
||||
CHAR_0 -> {
|
||||
cursor++
|
||||
if (cursor != limit) {
|
||||
c = ary[cursor]
|
||||
}
|
||||
}
|
||||
!in CHAR_0..CHAR_9 -> makeError(data, "Invalid Number", c)
|
||||
else -> {
|
||||
do {
|
||||
val digit = c - CHAR_0
|
||||
// double = 10.0 * double + digit
|
||||
long = 10 * long + digit
|
||||
digits++
|
||||
cursor++
|
||||
if (cursor == limit) break
|
||||
c = ary[cursor]
|
||||
} while (c in CHAR_0..CHAR_9)
|
||||
}
|
||||
}
|
||||
|
||||
var exponent = 0
|
||||
// If we find '.' we need to convert to double
|
||||
if (c == CHAR_DOT) {
|
||||
useDouble = true
|
||||
checkEOF(data, cursor)
|
||||
c = ary[++cursor]
|
||||
if (c < CHAR_0 || c > CHAR_9) {
|
||||
makeError(data, "Invalid Number", c)
|
||||
}
|
||||
do {
|
||||
// double = double * 10 + (tok - CHAR_0)
|
||||
long = 10 * long + (c - CHAR_0)
|
||||
digits++
|
||||
--exponent
|
||||
cursor++
|
||||
if (cursor == limit) break
|
||||
c = ary[cursor]
|
||||
} while (c in CHAR_0..CHAR_9)
|
||||
}
|
||||
|
||||
// If we find 'e' we need to convert to double
|
||||
if (c == CHAR_e || c == CHAR_E) {
|
||||
useDouble = true
|
||||
++cursor
|
||||
checkEOF(data, cursor)
|
||||
c = ary[cursor]
|
||||
var negativeExponent = false
|
||||
if (c == CHAR_MINUS) {
|
||||
++cursor
|
||||
checkEOF(data, cursor)
|
||||
negativeExponent = true
|
||||
c = ary[cursor]
|
||||
} else if (c == CHAR_PLUS) {
|
||||
++cursor
|
||||
checkEOF(data, cursor)
|
||||
c = ary[cursor]
|
||||
}
|
||||
if (c < CHAR_0 || c > CHAR_9) {
|
||||
makeError(data, "Missing exponent", c)
|
||||
}
|
||||
var exp = 0
|
||||
do {
|
||||
val digit = c - CHAR_0
|
||||
exp = 10 * exp + digit
|
||||
++cursor
|
||||
if (cursor == limit) break
|
||||
c = ary[cursor]
|
||||
} while (c in CHAR_0..CHAR_9)
|
||||
|
||||
exponent += if (negativeExponent) -exp else exp
|
||||
}
|
||||
|
||||
if (digits > 17 || exponent < -19 || exponent > 19) {
|
||||
// if the float number is not simple enough
|
||||
// we use language's Double parsing, which is slower but
|
||||
// produce more expected results for extreme numbers.
|
||||
val firstPos = readPos - 1
|
||||
val str = data.getString(firstPos, cursor - firstPos)
|
||||
if (useDouble) {
|
||||
double = str.toDouble()
|
||||
output[key] = double
|
||||
} else {
|
||||
long = str.toLong()
|
||||
output[key] = long
|
||||
}
|
||||
} else {
|
||||
// this happens on single numbers outside any object
|
||||
// or array
|
||||
if (useDouble || exponent != 0) {
|
||||
double = if (long == 0L) 0.0 else long.toDouble() * 10.0.pow(exponent)
|
||||
double *= sign
|
||||
output[key] = double
|
||||
} else {
|
||||
long *= sign
|
||||
output[key] = long
|
||||
}
|
||||
}
|
||||
readPos = cursor
|
||||
return if (useDouble) T_FLOAT else T_INT
|
||||
}
|
||||
|
||||
private fun parseString(data: ReadBuffer, key: String?): FlexBufferType {
|
||||
output[key] = readString(data)
|
||||
return T_STRING
|
||||
}
|
||||
|
||||
private fun readString(data: ReadBuffer): String {
|
||||
val limit = data.limit
|
||||
if (data is ArrayReadBuffer) {
|
||||
val ary = data.data()
|
||||
// enables range check elimination
|
||||
return readString(data, limit) { ary[it] }
|
||||
}
|
||||
return readString(data, limit) { data[it] }
|
||||
}
|
||||
|
||||
private inline fun readString(data: ReadBuffer, limit: Int, crossinline fetch: (Int) -> Byte): String {
|
||||
var cursorPos = readPos
|
||||
var foundEscape = false
|
||||
var currentChar: Byte = 0
|
||||
// we loop over every 4 bytes until find any non-plain char
|
||||
while (limit - cursorPos >= 4) {
|
||||
currentChar = fetch(cursorPos)
|
||||
if (!isPlainStringChar(currentChar)) {
|
||||
foundEscape = true
|
||||
break
|
||||
}
|
||||
currentChar = fetch(cursorPos + 1)
|
||||
if (!isPlainStringChar(currentChar)) {
|
||||
cursorPos += 1
|
||||
foundEscape = true
|
||||
break
|
||||
}
|
||||
currentChar = fetch(cursorPos + 2)
|
||||
if (!isPlainStringChar(currentChar)) {
|
||||
cursorPos += 2
|
||||
foundEscape = true
|
||||
break
|
||||
}
|
||||
currentChar = fetch(cursorPos + 3)
|
||||
if (!isPlainStringChar(currentChar)) {
|
||||
cursorPos += 3
|
||||
foundEscape = true
|
||||
break
|
||||
}
|
||||
cursorPos += 4
|
||||
}
|
||||
if (!foundEscape) {
|
||||
// if non-plain string char is not found we loop over
|
||||
// the remaining bytes
|
||||
while (true) {
|
||||
if (cursorPos >= limit) {
|
||||
error("Unexpected end of string")
|
||||
}
|
||||
currentChar = fetch(cursorPos)
|
||||
if (!isPlainStringChar(currentChar)) {
|
||||
break
|
||||
}
|
||||
++cursorPos
|
||||
}
|
||||
}
|
||||
if (currentChar == CHAR_DOUBLE_QUOTE) {
|
||||
val str = data.getString(readPos, cursorPos - readPos)
|
||||
readPos = cursorPos + 1
|
||||
return str
|
||||
}
|
||||
if (currentChar in 0..0x1f) {
|
||||
error("Illegal Codepoint")
|
||||
} else {
|
||||
// backslash or >0x7f
|
||||
return readStringSlow(data, currentChar, cursorPos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readStringSlow(data: ReadBuffer, first: Byte, lastPos: Int): String {
|
||||
var cursorPos = lastPos
|
||||
|
||||
var endOfString = lastPos
|
||||
while (true) {
|
||||
val pos = data.findFirst(CHAR_DOUBLE_QUOTE, endOfString)
|
||||
when {
|
||||
pos == -1 -> makeError(data, "Unexpected EOF, missing end of string '\"'", first)
|
||||
data[pos - 1] == CHAR_BACKSLASH && data[pos - 2] != CHAR_BACKSLASH -> {
|
||||
// here we are checking for double quotes preceded by backslash. eg \"
|
||||
// we have to look past pos -2 to make sure that the backlash is not
|
||||
// part of a previous escape, eg "\\"
|
||||
endOfString = pos + 1
|
||||
}
|
||||
else -> {
|
||||
endOfString = pos; break
|
||||
}
|
||||
}
|
||||
}
|
||||
// copy everything before the escape
|
||||
val builder = StringBuilder(data.getString(readPos, lastPos - readPos))
|
||||
while (true) {
|
||||
when (val pos = data.findFirst(CHAR_BACKSLASH, cursorPos, endOfString)) {
|
||||
-1 -> {
|
||||
val doubleQuotePos = data.findFirst(CHAR_DOUBLE_QUOTE, cursorPos)
|
||||
if (doubleQuotePos == -1) makeError(data, "Reached EOF before enclosing string", first)
|
||||
val rest = data.getString(cursorPos, doubleQuotePos - cursorPos)
|
||||
builder.append(rest)
|
||||
readPos = doubleQuotePos + 1
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
else -> {
|
||||
// we write everything up to \
|
||||
builder.append(data.getString(cursorPos, pos - cursorPos))
|
||||
val c = data[pos + 1]
|
||||
builder.append(readEscapedChar(data, c, pos))
|
||||
cursorPos = pos + if (c == CHAR_u) 6 else 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun isPlainStringChar(c: Byte): Boolean {
|
||||
val flags = parseFlags
|
||||
// return c in 0x20..0x7f && c != 0x22.toByte() && c != 0x5c.toByte()
|
||||
return (flags[c.toInt() and 0xFF] and 1) != 0.toByte()
|
||||
}
|
||||
|
||||
private inline fun isWhitespace(c: Byte): Boolean {
|
||||
val flags = parseFlags
|
||||
// return c == '\r'.toByte() || c == '\n'.toByte() || c == '\t'.toByte() || c == ' '.toByte()
|
||||
return (flags[c.toInt() and 0xFF] and 2) != 0.toByte()
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
readPos = 0
|
||||
output.clear()
|
||||
scopes.reset()
|
||||
}
|
||||
|
||||
private fun nextToken(data: ReadBuffer): Token {
|
||||
val scope = this.scopes.last
|
||||
|
||||
when (scope) {
|
||||
SCOPE_ARRAY_EMPTY -> this.scopes.last = SCOPE_ARRAY_FILLED
|
||||
SCOPE_ARRAY_FILLED -> {
|
||||
when (val c = skipWhitespace(data)) {
|
||||
CHAR_CLOSE_ARRAY -> return TOK_END_ARRAY
|
||||
CHAR_COMMA -> Unit
|
||||
else -> makeError(data, "Unfinished Array", c)
|
||||
}
|
||||
}
|
||||
SCOPE_OBJ_EMPTY, SCOPE_OBJ_FILLED -> {
|
||||
this.scopes.last = SCOPE_OBJ_KEY
|
||||
// Look for a comma before the next element.
|
||||
if (scope == SCOPE_OBJ_FILLED) {
|
||||
when (val c = skipWhitespace(data)) {
|
||||
CHAR_CLOSE_OBJECT -> return TOK_END_OBJECT
|
||||
CHAR_COMMA -> Unit
|
||||
else -> makeError(data, "Unfinished Object", c)
|
||||
}
|
||||
}
|
||||
return when (val c = skipWhitespace(data)) {
|
||||
CHAR_DOUBLE_QUOTE -> TOK_BEGIN_QUOTE
|
||||
CHAR_CLOSE_OBJECT -> if (scope != SCOPE_OBJ_FILLED) {
|
||||
TOK_END_OBJECT
|
||||
} else {
|
||||
makeError(data, "Expected Key", c)
|
||||
}
|
||||
else -> {
|
||||
makeError(data, "Expected Key/Value", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
SCOPE_OBJ_KEY -> {
|
||||
this.scopes.last = SCOPE_OBJ_FILLED
|
||||
when (val c = skipWhitespace(data)) {
|
||||
CHAR_COLON -> Unit
|
||||
else -> makeError(data, "Expect ${CHAR_COLON.print()}", c)
|
||||
}
|
||||
}
|
||||
SCOPE_DOC_EMPTY -> this.scopes.last = SCOPE_DOC_FILLED
|
||||
SCOPE_DOC_FILLED -> {
|
||||
val c = skipWhitespace(data)
|
||||
if (c != CHAR_EOF)
|
||||
makeError(data, "Root object already finished", c)
|
||||
return TOK_EOF
|
||||
}
|
||||
}
|
||||
|
||||
val c = skipWhitespace(data)
|
||||
when (c) {
|
||||
CHAR_CLOSE_ARRAY -> if (scope == SCOPE_ARRAY_EMPTY) return TOK_END_ARRAY
|
||||
CHAR_COLON -> makeError(data, "Unexpected character", c)
|
||||
CHAR_DOUBLE_QUOTE -> return TOK_BEGIN_QUOTE
|
||||
CHAR_OPEN_ARRAY -> return TOK_BEGIN_ARRAY
|
||||
CHAR_OPEN_OBJECT -> return TOK_BEGIN_OBJECT
|
||||
CHAR_t -> {
|
||||
checkEOF(data, readPos + 2)
|
||||
// 0x65757274 is equivalent to ['t', 'r', 'u', 'e' ] as a 4 byte Int
|
||||
if (data.getInt(readPos - 1) != 0x65757274) {
|
||||
makeError(data, "Expecting keyword \"true\"", c)
|
||||
}
|
||||
readPos += 3
|
||||
return TOK_TRUE
|
||||
}
|
||||
CHAR_n -> {
|
||||
checkEOF(data, readPos + 2)
|
||||
// 0x6c6c756e is equivalent to ['n', 'u', 'l', 'l' ] as a 4 byte Int
|
||||
if (data.getInt(readPos - 1) != 0x6c6c756e) {
|
||||
makeError(data, "Expecting keyword \"null\"", c)
|
||||
}
|
||||
readPos += 3
|
||||
return TOK_NULL
|
||||
}
|
||||
CHAR_f -> {
|
||||
checkEOF(data, readPos + 3)
|
||||
// 0x65736c61 is equivalent to ['a', 'l', 's', 'e' ] as a 4 byte Int
|
||||
if (data.getInt(readPos) != 0x65736c61) {
|
||||
makeError(data, "Expecting keyword \"false\"", c)
|
||||
}
|
||||
readPos += 4
|
||||
return TOK_FALSE
|
||||
}
|
||||
CHAR_0, CHAR_1, CHAR_2, CHAR_3, CHAR_4, CHAR_5,
|
||||
CHAR_6, CHAR_7, CHAR_8, CHAR_9, CHAR_MINUS -> return TOK_NUMBER.also {
|
||||
readPos-- // rewind one position so we don't lose first digit
|
||||
}
|
||||
}
|
||||
makeError(data, "Expecting element", c)
|
||||
}
|
||||
|
||||
// keeps increasing [readPos] until finds a non-whitespace byte
|
||||
private inline fun skipWhitespace(data: ReadBuffer): Byte {
|
||||
val limit = data.limit
|
||||
if (data is ArrayReadBuffer) {
|
||||
// enables range check elimination
|
||||
val ary = data.data()
|
||||
return skipWhitespace(limit) { ary[it] }
|
||||
}
|
||||
return skipWhitespace(limit) { data[it] }
|
||||
}
|
||||
|
||||
private inline fun skipWhitespace(limit: Int, crossinline fetch: (Int) -> Byte): Byte {
|
||||
var pos = readPos
|
||||
while (pos < limit) {
|
||||
val d = fetch(pos++)
|
||||
if (!isWhitespace(d)) {
|
||||
readPos = pos
|
||||
return d
|
||||
}
|
||||
}
|
||||
readPos = limit
|
||||
return CHAR_EOF
|
||||
}
|
||||
|
||||
// byte1 is expected to be first char before `\`
|
||||
private fun readEscapedChar(data: ReadBuffer, byte1: Byte, cursorPos: Int): Char {
|
||||
return when (byte1) {
|
||||
CHAR_u -> {
|
||||
checkEOF(data, cursorPos + 1 + 4)
|
||||
var result: Char = 0.toChar()
|
||||
var i = cursorPos + 2 // cursorPos is on '\\', cursorPos + 1 is 'u'
|
||||
val end = i + 4
|
||||
while (i < end) {
|
||||
val part: Byte = data[i]
|
||||
result = (result.code shl 4).toChar()
|
||||
result += when (part) {
|
||||
in CHAR_0..CHAR_9 -> part - CHAR_0
|
||||
in CHAR_a..CHAR_f -> part - CHAR_a + 10
|
||||
in CHAR_A..CHAR_F -> part - CHAR_A + 10
|
||||
else -> makeError(data, "Invalid utf8 escaped character", -1)
|
||||
}
|
||||
i++
|
||||
}
|
||||
result
|
||||
}
|
||||
CHAR_b -> '\b'
|
||||
CHAR_t -> '\t'
|
||||
CHAR_r -> '\r'
|
||||
CHAR_n -> '\n'
|
||||
CHAR_f -> 12.toChar() // '\f'
|
||||
CHAR_DOUBLE_QUOTE, CHAR_BACKSLASH, CHAR_FORWARDSLASH -> byte1.toInt().toChar()
|
||||
else -> makeError(data, "Invalid escape sequence.", byte1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Byte.print(): String = when (this) {
|
||||
in 0x21..0x7E -> "'${this.toInt().toChar()}'" // visible ascii chars
|
||||
CHAR_EOF -> "EOF"
|
||||
else -> "'0x${this.toString(16)}'"
|
||||
}
|
||||
|
||||
private inline fun makeError(data: ReadBuffer, msg: String, tok: Byte? = null): Nothing {
|
||||
val (line, column) = calculateErrorPosition(data, readPos)
|
||||
if (tok != null) {
|
||||
error("Error At ($line, $column): $msg, got ${tok.print()}")
|
||||
} else {
|
||||
error("Error At ($line, $column): $msg")
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun makeError(data: ReadBuffer, msg: String, tok: Token): Nothing {
|
||||
val (line, column) = calculateErrorPosition(data, readPos)
|
||||
error("Error At ($line, $column): $msg, got ${tok.print()}")
|
||||
}
|
||||
|
||||
private inline fun checkEOF(data: ReadBuffer, pos: Int) {
|
||||
if (pos >= data.limit)
|
||||
makeError(data, "Unexpected end of file", -1)
|
||||
}
|
||||
|
||||
private fun calculateErrorPosition(data: ReadBuffer, endPos: Int): Pair<Int, Int> {
|
||||
var line = 1
|
||||
var column = 1
|
||||
var current = 0
|
||||
while (current < endPos - 1) {
|
||||
if (data[current++] == CHAR_NEWLINE) {
|
||||
++line
|
||||
column = 1
|
||||
} else {
|
||||
++column
|
||||
}
|
||||
}
|
||||
return Pair(line, column)
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun Int.toPaddedHex(): String = "\\u${this.toString(16).padStart(4, '0')}"
|
||||
|
||||
private inline fun ReadWriteBuffer.jsonEscape(data: ReadBuffer, start: Int, size: Int) {
|
||||
val replacements = JSON_ESCAPE_CHARS
|
||||
put(CHAR_DOUBLE_QUOTE)
|
||||
var last = start
|
||||
val length: Int = size
|
||||
val ary = data.data()
|
||||
for (i in start until start + length) {
|
||||
val c = ary[i].toUByte()
|
||||
var replacement: ByteArray?
|
||||
if (c.toInt() < 128) {
|
||||
replacement = replacements[c.toInt()]
|
||||
if (replacement == null) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
if (last < i) {
|
||||
put(ary, last, i - last)
|
||||
}
|
||||
put(replacement, 0, replacement.size)
|
||||
last = i + 1
|
||||
}
|
||||
if (last < (last + length)) {
|
||||
put(ary, last, (start + length) - last)
|
||||
}
|
||||
put(CHAR_DOUBLE_QUOTE)
|
||||
}
|
||||
|
||||
// Following escape strategy defined in RFC7159.
|
||||
private val JSON_ESCAPE_CHARS: Array<ByteArray?> = arrayOfNulls<ByteArray>(128).apply {
|
||||
this['\n'.code] = "\\n".encodeToByteArray()
|
||||
this['\t'.code] = "\\t".encodeToByteArray()
|
||||
this['\r'.code] = "\\r".encodeToByteArray()
|
||||
this['\b'.code] = "\\b".encodeToByteArray()
|
||||
this[0x0c] = "\\f".encodeToByteArray()
|
||||
this['"'.code] = "\\\"".encodeToByteArray()
|
||||
this['\\'.code] = "\\\\".encodeToByteArray()
|
||||
for (i in 0..0x1f) {
|
||||
this[i] = "\\u${i.toPaddedHex()}".encodeToByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
// Scope is used to the define current space that the scanner is operating.
|
||||
@JvmInline
|
||||
private value class Scope(val id: Int)
|
||||
private val SCOPE_DOC_EMPTY = Scope(0)
|
||||
private val SCOPE_DOC_FILLED = Scope(1)
|
||||
private val SCOPE_OBJ_EMPTY = Scope(2)
|
||||
private val SCOPE_OBJ_KEY = Scope(3)
|
||||
private val SCOPE_OBJ_FILLED = Scope(4)
|
||||
private val SCOPE_ARRAY_EMPTY = Scope(5)
|
||||
private val SCOPE_ARRAY_FILLED = Scope(6)
|
||||
|
||||
// Keeps the stack state of the scopes being scanned. Currently defined to have a
|
||||
// max stack size of 22, as per tests cases defined in http://json.org/JSON_checker/
|
||||
private class ScopeStack(
|
||||
private val ary: IntArray = IntArray(22) { SCOPE_DOC_EMPTY.id },
|
||||
var lastPos: Int = 0
|
||||
) {
|
||||
var last: Scope
|
||||
get() = Scope(ary[lastPos])
|
||||
set(x) {
|
||||
ary[lastPos] = x.id
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
lastPos = 0
|
||||
ary[0] = SCOPE_DOC_EMPTY.id
|
||||
}
|
||||
|
||||
fun pop(): Scope {
|
||||
// println("Popping: ${last.print()}")
|
||||
return Scope(ary[lastPos--])
|
||||
}
|
||||
|
||||
fun push(scope: Scope): Scope {
|
||||
if (lastPos == ary.size - 1)
|
||||
error("Too much nesting reached. Max nesting is ${ary.size} levels")
|
||||
// println("PUSHING : ${scope.print()}")
|
||||
ary[++lastPos] = scope.id
|
||||
return scope
|
||||
}
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
private value class Token(val id: Int) {
|
||||
fun print(): String = when (this) {
|
||||
TOK_EOF -> "TOK_EOF"
|
||||
TOK_NONE -> "TOK_NONE"
|
||||
TOK_BEGIN_OBJECT -> "TOK_BEGIN_OBJECT"
|
||||
TOK_END_OBJECT -> "TOK_END_OBJECT"
|
||||
TOK_BEGIN_ARRAY -> "TOK_BEGIN_ARRAY"
|
||||
TOK_END_ARRAY -> "TOK_END_ARRAY"
|
||||
TOK_NUMBER -> "TOK_NUMBER"
|
||||
TOK_TRUE -> "TOK_TRUE"
|
||||
TOK_FALSE -> "TOK_FALSE"
|
||||
TOK_NULL -> "TOK_NULL"
|
||||
TOK_BEGIN_QUOTE -> "TOK_BEGIN_QUOTE"
|
||||
else -> this.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private val TOK_EOF = Token(-1)
|
||||
private val TOK_NONE = Token(0)
|
||||
private val TOK_BEGIN_OBJECT = Token(1)
|
||||
private val TOK_END_OBJECT = Token(2)
|
||||
private val TOK_BEGIN_ARRAY = Token(3)
|
||||
private val TOK_END_ARRAY = Token(4)
|
||||
private val TOK_NUMBER = Token(5)
|
||||
private val TOK_TRUE = Token(6)
|
||||
private val TOK_FALSE = Token(7)
|
||||
private val TOK_NULL = Token(8)
|
||||
private val TOK_BEGIN_QUOTE = Token(9)
|
||||
|
||||
private const val CHAR_NEWLINE = '\n'.code.toByte()
|
||||
private const val CHAR_OPEN_OBJECT = '{'.code.toByte()
|
||||
private const val CHAR_COLON = ':'.code.toByte()
|
||||
private const val CHAR_CLOSE_OBJECT = '}'.code.toByte()
|
||||
private const val CHAR_OPEN_ARRAY = '['.code.toByte()
|
||||
private const val CHAR_CLOSE_ARRAY = ']'.code.toByte()
|
||||
private const val CHAR_DOUBLE_QUOTE = '"'.code.toByte()
|
||||
private const val CHAR_BACKSLASH = '\\'.code.toByte()
|
||||
private const val CHAR_FORWARDSLASH = '/'.code.toByte()
|
||||
private const val CHAR_f = 'f'.code.toByte()
|
||||
private const val CHAR_a = 'a'.code.toByte()
|
||||
private const val CHAR_r = 'r'.code.toByte()
|
||||
private const val CHAR_t = 't'.code.toByte()
|
||||
private const val CHAR_n = 'n'.code.toByte()
|
||||
private const val CHAR_b = 'b'.code.toByte()
|
||||
private const val CHAR_e = 'e'.code.toByte()
|
||||
private const val CHAR_E = 'E'.code.toByte()
|
||||
private const val CHAR_u = 'u'.code.toByte()
|
||||
private const val CHAR_A = 'A'.code.toByte()
|
||||
private const val CHAR_F = 'F'.code.toByte()
|
||||
private const val CHAR_EOF = (-1).toByte()
|
||||
private const val CHAR_COMMA = ','.code.toByte()
|
||||
private const val CHAR_0 = '0'.code.toByte()
|
||||
private const val CHAR_1 = '1'.code.toByte()
|
||||
private const val CHAR_2 = '2'.code.toByte()
|
||||
private const val CHAR_3 = '3'.code.toByte()
|
||||
private const val CHAR_4 = '4'.code.toByte()
|
||||
private const val CHAR_5 = '5'.code.toByte()
|
||||
private const val CHAR_6 = '6'.code.toByte()
|
||||
private const val CHAR_7 = '7'.code.toByte()
|
||||
private const val CHAR_8 = '8'.code.toByte()
|
||||
private const val CHAR_9 = '9'.code.toByte()
|
||||
private const val CHAR_MINUS = '-'.code.toByte()
|
||||
private const val CHAR_PLUS = '+'.code.toByte()
|
||||
private const val CHAR_DOT = '.'.code.toByte()
|
||||
|
||||
// This template utilizes the One Definition Rule to create global arrays in a
|
||||
// header. As seen in:
|
||||
// https://github.com/chadaustin/sajson/blob/master/include/sajson.h
|
||||
// bit 0 (1) - set if: plain ASCII string character
|
||||
// bit 1 (2) - set if: whitespace
|
||||
// bit 4 (0x10) - set if: 0-9 e E .
|
||||
private val parseFlags = byteArrayOf(
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
|
||||
3, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11, 1, // 2
|
||||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 1, 1, 1, 1, 1, 1, // 3
|
||||
1, 1, 1, 1, 1, 0x11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 5
|
||||
1, 1, 1, 1, 1, 0x11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
||||
|
||||
// 128-255
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
)
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
fun <T> assertArrayEquals(expected: Array<out T>, actual: Array<out T>) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun assertArrayEquals(expected: IntArray, actual: IntArray) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun assertArrayEquals(expected: ShortArray, actual: ShortArray) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun assertArrayEquals(expected: LongArray, actual: LongArray) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun assertArrayEquals(expected: ByteArray, actual: ByteArray) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun assertArrayEquals(expected: DoubleArray, actual: DoubleArray) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun assertArrayEquals(expected: FloatArray, actual: FloatArray) =
|
||||
assertTrue(expected contentEquals actual, arrayFailMessage(expected, actual))
|
||||
|
||||
fun <T> arrayFailMessage(expected: Array<out T>, actual: Array<out T>): String =
|
||||
failMessage(expected.contentToString(), actual.contentToString())
|
||||
|
||||
fun arrayFailMessage(expected: IntArray, actual: IntArray): String =
|
||||
failMessage(expected.contentToString(), actual.contentToString())
|
||||
|
||||
fun arrayFailMessage(expected: ShortArray, actual: ShortArray): String =
|
||||
failMessage(expected.contentToString(), actual.contentToString())
|
||||
|
||||
fun arrayFailMessage(expected: LongArray, actual: LongArray): String =
|
||||
failMessage(expected.contentToString(), actual.contentToString())
|
||||
|
||||
fun failMessage(expected: String, actual: String): String =
|
||||
"Expected: $expected\nActual: $actual"
|
||||
|
||||
fun arrayFailMessage(expected: FloatArray, actual: FloatArray): String {
|
||||
return "Expected: ${expected.contentToString()}\nActual: ${actual.contentToString()}"
|
||||
}
|
||||
|
||||
fun arrayFailMessage(expected: DoubleArray, actual: DoubleArray): String {
|
||||
return "Expected: ${expected.contentToString()}\nActual: ${actual.contentToString()}"
|
||||
}
|
||||
|
||||
fun arrayFailMessage(expected: BooleanArray, actual: BooleanArray): String {
|
||||
return "Expected: ${expected.contentToString()}\nActual: ${actual.contentToString()}"
|
||||
}
|
||||
|
||||
fun arrayFailMessage(expected: ByteArray, actual: ByteArray): String {
|
||||
return "Expected: ${expected.contentToString()}\nActual: ${actual.contentToString()}"
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class BuffersTest {
|
||||
|
||||
@Test
|
||||
fun readBufferStringTest() {
|
||||
val text = "Hello world!"
|
||||
val bytes = text.encodeToByteArray()
|
||||
val fullRead = ArrayReadBuffer(bytes)
|
||||
val helloRead = ArrayReadBuffer(bytes, limit = 5)
|
||||
val worldRead = fullRead.slice(6, 6)
|
||||
|
||||
assertEquals(bytes.size, fullRead.limit)
|
||||
assertEquals(text, fullRead.getString(0, fullRead.limit))
|
||||
assertEquals("Hello" , helloRead.getString(0, helloRead.limit))
|
||||
assertEquals("world!" , worldRead.getString())
|
||||
assertEquals(fullRead.getString(0, 5) , helloRead.getString(0, helloRead.limit))
|
||||
assertEquals(fullRead.getString(6, 6) , worldRead.getString(0, worldRead.limit))
|
||||
|
||||
for (i in 0 until helloRead.limit) {
|
||||
assertEquals(fullRead[i], helloRead[i])
|
||||
}
|
||||
for (i in 0 until worldRead.limit) {
|
||||
assertEquals(fullRead[6 + i], worldRead[i])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readWriteBufferPrimitivesTest() {
|
||||
val text = "Hello world!"
|
||||
val bytes = text.encodeToByteArray()
|
||||
val wt = ArrayReadWriteBuffer(bytes)
|
||||
wt.requestCapacity(4096)
|
||||
wt.put("Tests")
|
||||
val str1 = wt.writePosition
|
||||
assertEquals("Tests world!", wt.getString(0, bytes.size))
|
||||
assertEquals("Tests", wt.getString(0, str1))
|
||||
wt.put(Int.MAX_VALUE)
|
||||
assertEquals(Int.MAX_VALUE, wt.getInt(str1))
|
||||
|
||||
val pos = wt.writePosition
|
||||
wt.put(Double.NEGATIVE_INFINITY)
|
||||
assertEquals(Double.NEGATIVE_INFINITY, wt.getDouble(pos))
|
||||
|
||||
val jap = " are really すごい!".encodeToByteArray()
|
||||
wt.writePosition = str1
|
||||
wt.put(jap)
|
||||
assertEquals("Tests are really すごい!", wt.getString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readWriteBufferGrowthTest() {
|
||||
val a = ArrayReadWriteBuffer(1)
|
||||
assertEquals(1, a.capacity)
|
||||
a.put(0.toByte())
|
||||
assertEquals(1, a.capacity)
|
||||
assertFailsWith(IndexOutOfBoundsException::class) { a.put(0xFF.toShort()) }
|
||||
a.requestCapacity(8)
|
||||
a.writePosition = 0
|
||||
a.put(0xFF.toShort())
|
||||
assertEquals(8, a.capacity)
|
||||
assertEquals(0xFF, a.getShort(0))
|
||||
|
||||
a.requestCapacity(8 + 12)
|
||||
a.put(ByteArray(12) { it.toByte() })
|
||||
|
||||
// we grow as power or two, so 20 jumps to 32
|
||||
assertEquals(32, a.capacity)
|
||||
a.requestCapacity(513, false)
|
||||
assertEquals(1024, a.capacity)
|
||||
a.requestCapacity(234, false)
|
||||
assertEquals(1024, a.capacity)
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ByteArrayTest {
|
||||
|
||||
@Test
|
||||
fun testByte() {
|
||||
val testSet = arrayOf(
|
||||
67.toByte() to byteArrayOf(67),
|
||||
Byte.MIN_VALUE to byteArrayOf(-128),
|
||||
Byte.MAX_VALUE to byteArrayOf(127),
|
||||
0.toByte() to byteArrayOf(0)
|
||||
)
|
||||
val data = ByteArray(1)
|
||||
testSet.forEach {
|
||||
data[0] = it.first
|
||||
assertArrayEquals(data, it.second)
|
||||
assertEquals(it.first, data[0])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShort() {
|
||||
val testSet = arrayOf(
|
||||
6712.toShort() to byteArrayOf(56, 26),
|
||||
Short.MIN_VALUE to byteArrayOf(0, -128),
|
||||
Short.MAX_VALUE to byteArrayOf(-1, 127),
|
||||
0.toShort() to byteArrayOf(0, 0,)
|
||||
)
|
||||
|
||||
val data = ByteArray(Short.SIZE_BYTES)
|
||||
testSet.forEach {
|
||||
data.setShort(0, it.first)
|
||||
assertArrayEquals(data, it.second)
|
||||
assertEquals(it.first, data.getShort(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInt() {
|
||||
val testSet = arrayOf(
|
||||
33333500 to byteArrayOf(-4, -96, -4, 1),
|
||||
Int.MIN_VALUE to byteArrayOf(0, 0, 0, -128),
|
||||
Int.MAX_VALUE to byteArrayOf(-1, -1, -1, 127),
|
||||
0 to byteArrayOf(0, 0, 0, 0)
|
||||
)
|
||||
val data = ByteArray(Int.SIZE_BYTES)
|
||||
testSet.forEach {
|
||||
data.setInt(0, it.first)
|
||||
assertArrayEquals(data, it.second)
|
||||
assertEquals(it.first, data.getInt(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLong() {
|
||||
val testSet = arrayOf(
|
||||
1234567123122890123L to byteArrayOf(-117, -91, 29, -23, 65, 16, 34, 17),
|
||||
-1L to byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1),
|
||||
Long.MIN_VALUE to byteArrayOf(0, 0, 0, 0, 0, 0, 0, -128),
|
||||
Long.MAX_VALUE to byteArrayOf(-1, -1, -1, -1, -1, -1, -1, 127),
|
||||
0L to byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0)
|
||||
)
|
||||
val data = ByteArray(Long.SIZE_BYTES)
|
||||
testSet.forEach {
|
||||
data.setLong(0, it.first)
|
||||
assertArrayEquals(data, it.second)
|
||||
assertEquals(it.first, data.getLong(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testULong() {
|
||||
val testSet = arrayOf(
|
||||
1234567123122890123UL to byteArrayOf(-117, -91, 29, -23, 65, 16, 34, 17),
|
||||
ULong.MIN_VALUE to byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
(-1L).toULong() to byteArrayOf(-1, -1, -1, -1, -1, -1, -1, -1),
|
||||
0UL to byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0)
|
||||
)
|
||||
val data = ByteArray(ULong.SIZE_BYTES)
|
||||
testSet.forEach {
|
||||
data.setULong(0, it.first)
|
||||
assertArrayEquals(it.second, data)
|
||||
assertEquals(it.first, data.getULong(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFloat() {
|
||||
val testSet = arrayOf(
|
||||
3545.56337f to byteArrayOf(4, -103, 93, 69),
|
||||
Float.MIN_VALUE to byteArrayOf(1, 0, 0, 0),
|
||||
Float.MAX_VALUE to byteArrayOf(-1, -1, 127, 127),
|
||||
0f to byteArrayOf(0, 0, 0, 0)
|
||||
)
|
||||
val data = ByteArray(Float.SIZE_BYTES)
|
||||
testSet.forEach {
|
||||
data.setFloat(0, it.first)
|
||||
assertArrayEquals(data, it.second)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDouble() {
|
||||
val testSet = arrayOf(
|
||||
123456.523423423412 to byteArrayOf(88, 61, -15, 95, 8, 36, -2, 64),
|
||||
Double.MIN_VALUE to byteArrayOf(1, 0, 0, 0, 0, 0, 0, 0),
|
||||
Double.MAX_VALUE to byteArrayOf(-1, -1, -1, -1, -1, -1, -17, 127),
|
||||
0.0 to byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0)
|
||||
)
|
||||
val data = ByteArray(Long.SIZE_BYTES)
|
||||
testSet.forEach {
|
||||
data.setDouble(0, it.first)
|
||||
assertArrayEquals(data, it.second)
|
||||
assertEquals(it.first, data.getDouble(0))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testString() {
|
||||
val testSet = "∮ E⋅da = Q"
|
||||
val encoded = testSet.encodeToByteArray()
|
||||
val data = ByteArray(encoded.size)
|
||||
data.setCharSequence(0, testSet)
|
||||
assertArrayEquals(encoded, data)
|
||||
assertEquals(testSet, data.getString(0, encoded.size))
|
||||
}
|
||||
}
|
||||
|
||||
+575
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import Attacker
|
||||
import AttackerOffsetArray
|
||||
import CharacterEArray
|
||||
import dictionaryLookup.LongFloatEntry
|
||||
import dictionaryLookup.LongFloatMap
|
||||
import Movie
|
||||
import dictionaryLookup.LongFloatEntryOffsetArray
|
||||
import myGame.example.*
|
||||
import myGame.example.Test.Companion.createTest
|
||||
import optionalScalars.OptionalByte
|
||||
import optionalScalars.ScalarStuff
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
class FlatBufferBuilderTest {
|
||||
|
||||
@Test
|
||||
fun testSingleTable() {
|
||||
val fbb = FlatBufferBuilder()
|
||||
val name = fbb.createString("Frodo")
|
||||
val invValues = ubyteArrayOf(10u, 11u, 12u, 13u, 14u)
|
||||
val inv = Monster.createInventoryVector(fbb, invValues)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addPos(
|
||||
fbb, Vec3.createVec3(
|
||||
fbb, 1.0f, 2.0f, 3.0f, 3.0,
|
||||
Color.Green, 5.toShort(), 6.toByte()
|
||||
)
|
||||
)
|
||||
Monster.addHp(fbb, 80.toShort())
|
||||
Monster.addName(fbb, name)
|
||||
Monster.addMana(fbb, 150)
|
||||
Monster.addInventory(fbb, inv)
|
||||
Monster.addTestType(fbb, AnyE.Monster)
|
||||
Monster.addTestbool(fbb, true)
|
||||
Monster.addTesthashu32Fnv1(fbb, (Int.MAX_VALUE + 1L).toUInt())
|
||||
val root = Monster.endMonster(fbb)
|
||||
fbb.finish(root)
|
||||
|
||||
val monster = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monster.name, "Frodo")
|
||||
assertEquals(monster.mana, 150.toShort())
|
||||
assertEquals(monster.hp, 80)
|
||||
|
||||
val pos = monster.pos!!
|
||||
assertEquals(monster.inventory(0), invValues[0])
|
||||
assertEquals(monster.inventory(1), invValues[1])
|
||||
assertEquals(monster.inventory(2), invValues[2])
|
||||
assertEquals(monster.inventory(3), invValues[3])
|
||||
assertEquals(pos.x, 1.0f)
|
||||
assertEquals(pos.y, 2.0f)
|
||||
assertEquals(pos.z, 3.0f)
|
||||
assertEquals(pos.test1, 3.0)
|
||||
assertEquals(pos.test2, Color.Green)
|
||||
assertEquals(pos.test3!!.a, 5.toShort())
|
||||
assertEquals(pos.test3!!.b, 6.toByte())
|
||||
|
||||
val inventoryBuffer = monster.inventoryAsBuffer()
|
||||
assertEquals(invValues.size, inventoryBuffer.limit)
|
||||
for (i in invValues.indices) {
|
||||
assertEquals(invValues[i], inventoryBuffer.getUByte(i))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSortedVector() {
|
||||
val fbb = FlatBufferBuilder()
|
||||
val names = arrayOf(fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma"))
|
||||
val monsters = MonsterOffsetArray(3) {
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, names[it])
|
||||
Monster.endMonster(fbb)
|
||||
}
|
||||
val ary = Monster.createTestarrayoftablesVector(fbb, monsters)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, names[0])
|
||||
Monster.addTestarrayoftables(fbb, ary)
|
||||
val root = Monster.endMonster(fbb)
|
||||
fbb.finish(root)
|
||||
val a = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(a.name, "Frodo")
|
||||
assertEquals(a.testarrayoftablesLength, 3)
|
||||
val monster0 = a.testarrayoftables(0)!!
|
||||
val monster1 = a.testarrayoftables(1)!!
|
||||
val monster2 = a.testarrayoftables(2)!!
|
||||
assertEquals(monster0.name, "Frodo")
|
||||
assertEquals(monster1.name, "Barney")
|
||||
assertEquals(monster2.name, "Wilma")
|
||||
|
||||
// test AsBuffer feature
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateBufferVector() {
|
||||
val fbb = FlatBufferBuilder(16)
|
||||
val str = fbb.createString("MyMonster")
|
||||
val inventory = ubyteArrayOf(0u, 1u, 2u, 3u, 4u, 5u, 6u, 88u, 99u, 122u, 1u)
|
||||
val vec = Monster.createInventoryVector(fbb, inventory)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addInventory(fbb, vec)
|
||||
Monster.addName(fbb, str)
|
||||
val monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
val monsterObject: Monster = Monster.asRoot(fbb.dataBuffer())
|
||||
val iBuffer = monsterObject.inventoryAsBuffer()
|
||||
|
||||
assertEquals(monsterObject.inventoryLength, inventory.size)
|
||||
assertEquals(iBuffer.limit, inventory.size)
|
||||
|
||||
for (i in inventory.indices) {
|
||||
assertEquals(inventory[i], monsterObject.inventory(i))
|
||||
assertEquals(inventory[i], iBuffer.getUByte(i))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateUninitializedVector() {
|
||||
val fbb = FlatBufferBuilder(16)
|
||||
val str = fbb.createString("MyMonster")
|
||||
val inventory = byteArrayOf(10, 11, 12, 13, 14)
|
||||
val uninitializedBuffer = fbb.createUnintializedVector(1, inventory.size, 1)
|
||||
for (i in inventory) {
|
||||
uninitializedBuffer.put(i)
|
||||
}
|
||||
val vec = fbb.endVector<UByte>()
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addInventory(fbb, vec)
|
||||
Monster.addName(fbb, str)
|
||||
val monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
val monsterObject: Monster = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(inventory[1].toUByte(), monsterObject.inventory(1))
|
||||
assertEquals(inventory.size, monsterObject.inventoryLength)
|
||||
val inventoryBuffer = monsterObject.inventoryAsBuffer()
|
||||
assertEquals(inventory[1].toInt().toUByte(), inventoryBuffer.getUByte(1))
|
||||
assertEquals(inventory.size, inventoryBuffer.limit)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBuilderBasics() {
|
||||
val fbb = FlatBufferBuilder()
|
||||
val names = arrayOf(fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma"))
|
||||
val off = Array<Offset<Monster>>(3) { Offset(0) }
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, names[0])
|
||||
off[0] = Monster.endMonster(fbb)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, names[1])
|
||||
off[1] = Monster.endMonster(fbb)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, names[2])
|
||||
off[2] = Monster.endMonster(fbb)
|
||||
val sortMons = fbb.createSortedVectorOfTables(Monster(), off)
|
||||
|
||||
// We set up the same values as monsterdata.json:
|
||||
|
||||
val inv = Monster.createInventoryVector(fbb, byteArrayOf(0,1,2,3,4).toUByteArray())
|
||||
|
||||
val fred = fbb.createString("Fred")
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, fred)
|
||||
val mon2 = Monster.endMonster(fbb)
|
||||
|
||||
Monster.startTest4Vector(fbb, 2)
|
||||
createTest(fbb, 10.toShort(), 20.toByte())
|
||||
createTest(fbb, 30.toShort(), 40.toByte())
|
||||
val test4 = fbb.endVector<myGame.example.Test>()
|
||||
|
||||
val strings = StringOffsetArray(2) { fbb.createString("test$it") }
|
||||
val testArrayOfString =
|
||||
Monster.createTestarrayofstringVector(fbb, strings)
|
||||
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, names[0])
|
||||
Monster.addPos(fbb, Vec3.createVec3(
|
||||
fbb, 1.0f, 2.0f, 3.0f, 3.0,
|
||||
Color.Green, 5.toShort(), 6.toByte()
|
||||
))
|
||||
Monster.addHp(fbb, 80)
|
||||
Monster.addMana(fbb, 150)
|
||||
Monster.addInventory(fbb, inv)
|
||||
Monster.addTestType(fbb, AnyE.Monster)
|
||||
Monster.addTest(fbb, mon2.toUnion())
|
||||
Monster.addTest4(fbb, test4)
|
||||
Monster.addTestarrayofstring(fbb, testArrayOfString)
|
||||
Monster.addTestbool(fbb, true)
|
||||
Monster.addTesthashu32Fnv1(fbb, (Int.MAX_VALUE + 1L).toUInt())
|
||||
Monster.addTestarrayoftables(fbb, sortMons)
|
||||
val mon = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, mon)
|
||||
//Attempt to mutate Monster fields and check whether the buffer has been mutated properly
|
||||
// revert to original values after testing
|
||||
val monster = Monster.asRoot(fbb.dataBuffer())
|
||||
|
||||
// mana is optional and does not exist in the buffer so the mutation should fail
|
||||
// the mana field should retain its default value
|
||||
assertEquals(monster.mana, 150.toShort())
|
||||
assertEquals(monster.hp, 80)
|
||||
|
||||
// Accessing a vector of sorted by the key tables
|
||||
assertEquals(monster.testarrayoftables(0)!!.name, "Barney")
|
||||
assertEquals(monster.testarrayoftables(1)!!.name, "Frodo")
|
||||
assertEquals(monster.testarrayoftables(2)!!.name, "Wilma")
|
||||
|
||||
// Example of searching for a table by the key
|
||||
assertEquals(monster.testarrayoftablesByKey("Frodo")!!.name, "Frodo")
|
||||
assertEquals(monster.testarrayoftablesByKey("Barney")!!.name, "Barney")
|
||||
assertEquals(monster.testarrayoftablesByKey("Wilma")!!.name, "Wilma")
|
||||
|
||||
for (i in 0 until monster.inventoryLength) {
|
||||
assertEquals(monster.inventory(i), (i).toUByte())
|
||||
}
|
||||
|
||||
// get a struct field and edit one of its fields
|
||||
val pos2 = monster.pos!!
|
||||
assertEquals(pos2.x, 1.0f)
|
||||
assertEquals(pos2.test2, Color.Green)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testVectorOfUnions() {
|
||||
val fbb = FlatBufferBuilder()
|
||||
val swordAttackDamage = 1
|
||||
val attacker = Attacker.createAttacker(fbb, swordAttackDamage).toUnion()
|
||||
val attackers = UnionOffsetArray(1) { attacker }
|
||||
val characters = CharacterEArray(1)
|
||||
characters[0] = CharacterE.MuLan.value
|
||||
|
||||
Movie.finishMovieBuffer(
|
||||
fbb,
|
||||
Movie.createMovie(
|
||||
fbb,
|
||||
CharacterE.MuLan,
|
||||
attacker,
|
||||
Movie.createCharactersTypeVector(fbb, characters),
|
||||
Movie.createCharactersVector(fbb, attackers)
|
||||
)
|
||||
)
|
||||
|
||||
val movie: Movie = Movie.asRoot(fbb.dataBuffer())
|
||||
|
||||
|
||||
|
||||
assertEquals(movie.charactersTypeLength, 1)
|
||||
assertEquals(movie.charactersLength, 1)
|
||||
|
||||
assertEquals(movie.charactersType(0), CharacterE.MuLan)
|
||||
assertEquals((movie.characters(Attacker(), 0) as Attacker).swordAttackDamage, swordAttackDamage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestVectorOfBytes() {
|
||||
val fbb = FlatBufferBuilder(16)
|
||||
var str = fbb.createString("ByteMonster")
|
||||
val data = ubyteArrayOf(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u)
|
||||
var offset = Monster.createInventoryVector(fbb, data)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
var monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
|
||||
val monsterObject = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals("ByteMonster", monsterObject.name)
|
||||
assertEquals(data.size, monsterObject.inventoryLength)
|
||||
assertEquals(monsterObject.inventory(4), data[4])
|
||||
offset = fbb.createByteVector(data.toByteArray()) as VectorOffset<UByte> // TODO: fix me
|
||||
str = fbb.createString("ByteMonster")
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
|
||||
val monsterObject2 = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monsterObject2.inventoryLength, data.size)
|
||||
for (i in data.indices) {
|
||||
assertEquals(monsterObject2.inventory(i), data[i])
|
||||
}
|
||||
fbb.clear()
|
||||
offset = fbb.createByteVector(data.toByteArray(), 3, 4) as VectorOffset<UByte>
|
||||
str = fbb.createString("ByteMonster")
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
|
||||
val monsterObject3 = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monsterObject3.inventoryLength, 4)
|
||||
assertEquals(monsterObject3.inventory(0), data[3])
|
||||
fbb.clear()
|
||||
offset = Monster.createInventoryVector(fbb, data)
|
||||
str = fbb.createString("ByteMonster")
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
|
||||
val monsterObject4 = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monsterObject4.inventoryLength, data.size)
|
||||
assertEquals(monsterObject4.inventory(8), 8u)
|
||||
fbb.clear()
|
||||
|
||||
val largeData = ByteArray(1024)
|
||||
offset = fbb.createByteVector(largeData) as VectorOffset<UByte> //TODO: fix me
|
||||
str = fbb.createString("ByteMonster")
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
|
||||
val monsterObject5 = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monsterObject5.inventoryLength, largeData.size)
|
||||
assertEquals(monsterObject5.inventory(25), largeData[25].toUByte())
|
||||
fbb.clear()
|
||||
|
||||
var bb = ArrayReadBuffer(largeData, 512)
|
||||
offset = fbb.createByteVector(bb) as VectorOffset<UByte> //TODO: fix me
|
||||
str = fbb.createString("ByteMonster")
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
val monsterObject6 = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monsterObject6.inventoryLength, 512)
|
||||
assertEquals(monsterObject6.inventory(0), largeData[0].toUByte())
|
||||
fbb.clear()
|
||||
|
||||
bb = ArrayReadBuffer(largeData, largeData.size - 216)
|
||||
val stringBuffer = ArrayReadBuffer("AlreadyBufferedString".encodeToByteArray())
|
||||
offset = fbb.createByteVector(bb) as VectorOffset<UByte> //TODO: fix me
|
||||
str = fbb.createString(stringBuffer)
|
||||
Monster.startMonster(fbb)
|
||||
Monster.addName(fbb, str)
|
||||
Monster.addInventory(fbb, offset)
|
||||
monster1 = Monster.endMonster(fbb)
|
||||
Monster.finishMonsterBuffer(fbb, monster1)
|
||||
|
||||
val monsterObject7 = Monster.asRoot(fbb.dataBuffer())
|
||||
assertEquals(monsterObject7.inventoryLength, 216)
|
||||
assertEquals("AlreadyBufferedString", monsterObject7.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnums() {
|
||||
assertEquals(Color.name(Color.Red), "Red")
|
||||
assertEquals(Color.name(Color.Blue), "Blue")
|
||||
assertEquals(AnyE.name(AnyE.None), "NONE")
|
||||
assertEquals(AnyE.name(AnyE.Monster), "Monster")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSharedStringPool() {
|
||||
val fb = FlatBufferBuilder(1)
|
||||
val testString = "My string"
|
||||
val offset = fb.createSharedString(testString)
|
||||
for (i in 0..9) {
|
||||
assertEquals(offset, fb.createSharedString(testString))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScalarOptional() {
|
||||
val fbb = FlatBufferBuilder(1)
|
||||
ScalarStuff.startScalarStuff(fbb)
|
||||
var pos = ScalarStuff.endScalarStuff(fbb)
|
||||
fbb.finish(pos)
|
||||
var scalarStuff: ScalarStuff = ScalarStuff.asRoot(fbb.dataBuffer())
|
||||
assertEquals(scalarStuff.justI8, 0.toByte())
|
||||
assertEquals(scalarStuff.maybeI8, null)
|
||||
assertEquals(scalarStuff.defaultI8, 42.toByte())
|
||||
assertEquals(scalarStuff.justU8, 0u)
|
||||
assertEquals(scalarStuff.maybeU8, null)
|
||||
assertEquals(scalarStuff.defaultU8, 42u)
|
||||
assertEquals(scalarStuff.justI16, 0.toShort())
|
||||
assertEquals(scalarStuff.maybeI16, null)
|
||||
assertEquals(scalarStuff.defaultI16, 42.toShort())
|
||||
assertEquals(scalarStuff.justU16, 0u)
|
||||
assertEquals(scalarStuff.maybeU16, null)
|
||||
assertEquals(scalarStuff.defaultU16, 42u)
|
||||
assertEquals(scalarStuff.justI32, 0)
|
||||
assertEquals(scalarStuff.maybeI32, null)
|
||||
assertEquals(scalarStuff.defaultI32, 42)
|
||||
assertEquals(scalarStuff.justU32, 0u)
|
||||
assertEquals(scalarStuff.maybeU32, null)
|
||||
assertEquals(scalarStuff.defaultU32, 42u)
|
||||
assertEquals(scalarStuff.justI64, 0L)
|
||||
assertEquals(scalarStuff.maybeI64, null)
|
||||
assertEquals(scalarStuff.defaultI64, 42L)
|
||||
assertEquals(scalarStuff.justU64, 0UL)
|
||||
assertEquals(scalarStuff.maybeU64, null)
|
||||
assertEquals(scalarStuff.defaultU64, 42UL)
|
||||
assertEquals(scalarStuff.justF32, 0.0f)
|
||||
assertEquals(scalarStuff.maybeF32, null)
|
||||
assertEquals(scalarStuff.defaultF32, 42.0f)
|
||||
assertEquals(scalarStuff.justF64, 0.0)
|
||||
assertEquals(scalarStuff.maybeF64, null)
|
||||
assertEquals(scalarStuff.defaultF64, 42.0)
|
||||
assertEquals(scalarStuff.justBool, false)
|
||||
assertEquals(scalarStuff.maybeBool, null)
|
||||
assertEquals(scalarStuff.defaultBool, true)
|
||||
assertEquals(scalarStuff.justEnum, OptionalByte.None)
|
||||
assertEquals(scalarStuff.maybeEnum, null)
|
||||
assertEquals(scalarStuff.defaultEnum, OptionalByte.One)
|
||||
fbb.clear()
|
||||
ScalarStuff.startScalarStuff(fbb)
|
||||
ScalarStuff.addJustI8(fbb, 5.toByte())
|
||||
ScalarStuff.addMaybeI8(fbb, 5.toByte())
|
||||
ScalarStuff.addDefaultI8(fbb, 5.toByte())
|
||||
ScalarStuff.addJustU8(fbb, 6u)
|
||||
ScalarStuff.addMaybeU8(fbb, 6u)
|
||||
ScalarStuff.addDefaultU8(fbb, 6u)
|
||||
ScalarStuff.addJustI16(fbb, 7.toShort())
|
||||
ScalarStuff.addMaybeI16(fbb, 7.toShort())
|
||||
ScalarStuff.addDefaultI16(fbb, 7.toShort())
|
||||
ScalarStuff.addJustU16(fbb, 8u)
|
||||
ScalarStuff.addMaybeU16(fbb, 8u)
|
||||
ScalarStuff.addDefaultU16(fbb, 8u)
|
||||
ScalarStuff.addJustI32(fbb, 9)
|
||||
ScalarStuff.addMaybeI32(fbb, 9)
|
||||
ScalarStuff.addDefaultI32(fbb, 9)
|
||||
ScalarStuff.addJustU32(fbb, 10u)
|
||||
ScalarStuff.addMaybeU32(fbb, 10u)
|
||||
ScalarStuff.addDefaultU32(fbb, 10u)
|
||||
ScalarStuff.addJustI64(fbb, 11L)
|
||||
ScalarStuff.addMaybeI64(fbb, 11L)
|
||||
ScalarStuff.addDefaultI64(fbb, 11L)
|
||||
ScalarStuff.addJustU64(fbb, 12UL)
|
||||
ScalarStuff.addMaybeU64(fbb, 12UL)
|
||||
ScalarStuff.addDefaultU64(fbb, 12UL)
|
||||
ScalarStuff.addJustF32(fbb, 13.0f)
|
||||
ScalarStuff.addMaybeF32(fbb, 13.0f)
|
||||
ScalarStuff.addDefaultF32(fbb, 13.0f)
|
||||
ScalarStuff.addJustF64(fbb, 14.0)
|
||||
ScalarStuff.addMaybeF64(fbb, 14.0)
|
||||
ScalarStuff.addDefaultF64(fbb, 14.0)
|
||||
ScalarStuff.addJustBool(fbb, true)
|
||||
ScalarStuff.addMaybeBool(fbb, true)
|
||||
ScalarStuff.addDefaultBool(fbb, true)
|
||||
ScalarStuff.addJustEnum(fbb, OptionalByte.Two)
|
||||
ScalarStuff.addMaybeEnum(fbb, OptionalByte.Two)
|
||||
ScalarStuff.addDefaultEnum(fbb, OptionalByte.Two)
|
||||
pos = ScalarStuff.endScalarStuff(fbb)
|
||||
fbb.finish(pos)
|
||||
scalarStuff = ScalarStuff.asRoot(fbb.dataBuffer())
|
||||
assertEquals(scalarStuff.justI8, 5.toByte())
|
||||
assertEquals(scalarStuff.maybeI8, 5.toByte())
|
||||
assertEquals(scalarStuff.defaultI8, 5.toByte())
|
||||
assertEquals(scalarStuff.justU8, 6u)
|
||||
assertEquals(scalarStuff.maybeU8, 6u)
|
||||
assertEquals(scalarStuff.defaultU8, 6u)
|
||||
assertEquals(scalarStuff.justI16, 7.toShort())
|
||||
assertEquals(scalarStuff.maybeI16, 7.toShort())
|
||||
assertEquals(scalarStuff.defaultI16, 7.toShort())
|
||||
assertEquals(scalarStuff.justU16, 8u)
|
||||
assertEquals(scalarStuff.maybeU16, 8u)
|
||||
assertEquals(scalarStuff.defaultU16, 8u)
|
||||
assertEquals(scalarStuff.justI32, 9)
|
||||
assertEquals(scalarStuff.maybeI32, 9)
|
||||
assertEquals(scalarStuff.defaultI32, 9)
|
||||
assertEquals(scalarStuff.justU32, 10u)
|
||||
assertEquals(scalarStuff.maybeU32, 10u)
|
||||
assertEquals(scalarStuff.defaultU32, 10u)
|
||||
assertEquals(scalarStuff.justI64, 11L)
|
||||
assertEquals(scalarStuff.maybeI64, 11L)
|
||||
assertEquals(scalarStuff.defaultI64, 11L)
|
||||
assertEquals(scalarStuff.justU64, 12UL)
|
||||
assertEquals(scalarStuff.maybeU64, 12UL)
|
||||
assertEquals(scalarStuff.defaultU64, 12UL)
|
||||
assertEquals(scalarStuff.justF32, 13.0f)
|
||||
assertEquals(scalarStuff.maybeF32, 13.0f)
|
||||
assertEquals(scalarStuff.defaultF32, 13.0f)
|
||||
assertEquals(scalarStuff.justF64, 14.0)
|
||||
assertEquals(scalarStuff.maybeF64, 14.0)
|
||||
assertEquals(scalarStuff.defaultF64, 14.0)
|
||||
assertEquals(scalarStuff.justBool, true)
|
||||
assertEquals(scalarStuff.maybeBool, true)
|
||||
assertEquals(scalarStuff.defaultBool, true)
|
||||
assertEquals(scalarStuff.justEnum, OptionalByte.Two)
|
||||
assertEquals(scalarStuff.maybeEnum, OptionalByte.Two)
|
||||
assertEquals(scalarStuff.defaultEnum, OptionalByte.Two)
|
||||
}
|
||||
|
||||
// @todo Seems like nesting code generation is broken for all generators.
|
||||
// disabling test for now.
|
||||
// @Test
|
||||
// fun testNamespaceNesting() {
|
||||
// // reference / manipulate these to verify compilation
|
||||
// val fbb = FlatBufferBuilder(1)
|
||||
// TableInNestedNS.startTableInNestedNS(fbb)
|
||||
// TableInNestedNS.addFoo(fbb, 1234)
|
||||
// val nestedTableOff = TableInNestedNS.endTableInNestedNs(fbb)
|
||||
// TableInFirstNS.startTableInFirstNS(fbb)
|
||||
// TableInFirstNS.addFooTable(fbb, nestedTableOff)
|
||||
// TableInFirstNS.endTableInFirstNs(fbb)
|
||||
// }
|
||||
|
||||
@Test
|
||||
fun testNestedFlatBuffer() {
|
||||
val nestedMonsterName = "NestedMonsterName"
|
||||
val nestedMonsterHp: Short = 600
|
||||
val nestedMonsterMana: Short = 1024
|
||||
val fbb1 = FlatBufferBuilder(16)
|
||||
val str1 = fbb1.createString(nestedMonsterName)
|
||||
Monster.startMonster(fbb1)
|
||||
Monster.addName(fbb1, str1)
|
||||
Monster.addHp(fbb1, nestedMonsterHp)
|
||||
Monster.addMana(fbb1, nestedMonsterMana)
|
||||
val monster1 = Monster.endMonster(fbb1)
|
||||
Monster.finishMonsterBuffer(fbb1, monster1)
|
||||
val fbb1Bytes: ByteArray = fbb1.sizedByteArray()
|
||||
val fbb2 = FlatBufferBuilder(16)
|
||||
val str2 = fbb2.createString("My Monster")
|
||||
val nestedBuffer = Monster.createTestnestedflatbufferVector(fbb2, fbb1Bytes.toUByteArray())
|
||||
Monster.startMonster(fbb2)
|
||||
Monster.addName(fbb2, str2)
|
||||
Monster.addHp(fbb2, 50.toShort())
|
||||
Monster.addMana(fbb2, 32.toShort())
|
||||
Monster.addTestnestedflatbuffer(fbb2, nestedBuffer)
|
||||
val monster = Monster.endMonster(fbb2)
|
||||
Monster.finishMonsterBuffer(fbb2, monster)
|
||||
|
||||
// Now test the data extracted from the nested buffer
|
||||
val mons = Monster.asRoot(fbb2.dataBuffer())
|
||||
val nestedMonster = mons.testnestedflatbufferAsMonster
|
||||
assertEquals(nestedMonsterMana, nestedMonster!!.mana)
|
||||
assertEquals(nestedMonsterHp, nestedMonster.hp)
|
||||
assertEquals(nestedMonsterName, nestedMonster.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDictionaryLookup() {
|
||||
val fbb = FlatBufferBuilder(16)
|
||||
val lfIndex = LongFloatEntry.createLongFloatEntry(fbb, 0, 99.0f)
|
||||
val vectorEntriesIdx = LongFloatMap.createEntriesVector(fbb, LongFloatEntryOffsetArray(1) { lfIndex })
|
||||
val rootIdx = LongFloatMap.createLongFloatMap(fbb, vectorEntriesIdx)
|
||||
LongFloatMap.finishLongFloatMapBuffer(fbb, rootIdx)
|
||||
val map: LongFloatMap = LongFloatMap.asRoot(fbb.dataBuffer())
|
||||
|
||||
assertEquals(1, map.entriesLength)
|
||||
|
||||
val e: LongFloatEntry = map.entries(0)!!
|
||||
assertEquals(0L, e.key)
|
||||
assertEquals(99.0f, e.value)
|
||||
val e2: LongFloatEntry = map.entriesByKey(0)!!
|
||||
assertEquals(0L, e2.key)
|
||||
assertEquals(99.0f, e2.value)
|
||||
}
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import com.google.flatbuffers.kotlin.FlexBuffersBuilder.Companion.SHARE_NONE
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class FlexBuffersTest {
|
||||
|
||||
@Test
|
||||
fun testWriteInt() {
|
||||
val values = listOf(
|
||||
Byte.MAX_VALUE.toLong() to 3,
|
||||
Short.MAX_VALUE.toLong() to 4,
|
||||
Int.MAX_VALUE.toLong() to 6,
|
||||
Long.MAX_VALUE to 10
|
||||
)
|
||||
val builder = FlexBuffersBuilder()
|
||||
values.forEach {
|
||||
builder.clear()
|
||||
builder.put(it.first)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertEquals(it.second, data.limit)
|
||||
assertEquals(it.first, ref.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteUInt() {
|
||||
val values = listOf(
|
||||
UByte.MAX_VALUE.toULong() to 3,
|
||||
UShort.MAX_VALUE.toULong() to 4,
|
||||
UInt.MAX_VALUE.toULong() to 6,
|
||||
ULong.MAX_VALUE to 10
|
||||
)
|
||||
val builder = FlexBuffersBuilder()
|
||||
values.forEach {
|
||||
builder.clear()
|
||||
builder.put(it.first)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertEquals(it.second, data.limit)
|
||||
assertEquals(it.first, ref.toULong())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWriteString() {
|
||||
val text = "Ḧ̵̘́ȩ̵̐l̶̿͜l̶͚͝o̷̦̚ ̷̫̊w̴̤͊ö̸̞́r̴͎̾l̷͚̐d̶̰̍"
|
||||
val builder = FlexBuffersBuilder()
|
||||
builder.put(text)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
assertEquals(text, ref.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInt8Array() {
|
||||
val ary = intArrayOf(1, 2, 3, 4)
|
||||
val builder = FlexBuffersBuilder()
|
||||
builder.put(intArrayOf(1, 2, 3, 4))
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertEquals(8, data.limit)
|
||||
assertArrayEquals(ary, ref.toIntArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShortArray() {
|
||||
val builder = FlexBuffersBuilder(ArrayReadWriteBuffer(20))
|
||||
val numbers = ShortArray(10) { it.toShort() }
|
||||
builder.put(numbers)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
assertArrayEquals(numbers, ref.toShortArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHugeArray() {
|
||||
val builder = FlexBuffersBuilder()
|
||||
val numbers = IntArray(1024) { it }
|
||||
builder.put(numbers)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
assertArrayEquals(numbers, ref.toIntArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFloatArray() {
|
||||
val builder = FlexBuffersBuilder()
|
||||
val numbers = FloatArray(1024) { it * 0.05f }
|
||||
builder.put(numbers)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
assertArrayEquals(numbers, ref.toFloatArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoubleArray() {
|
||||
val builder = FlexBuffersBuilder()
|
||||
val numbers = DoubleArray(1024) { it * 0.0005 }
|
||||
builder.put(numbers)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
assertArrayEquals(numbers, ref.toDoubleArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongArray() {
|
||||
val ary: LongArray = longArrayOf(0, Short.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong(), Long.MAX_VALUE)
|
||||
val builder = FlexBuffersBuilder()
|
||||
builder.put(ary)
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertArrayEquals(ary, ref.toLongArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStringArray() {
|
||||
val ary = Array(5) { "Hello world number: $it" }
|
||||
val builder = FlexBuffersBuilder(ArrayReadWriteBuffer(20), SHARE_NONE)
|
||||
builder.putVector {
|
||||
ary.forEach { put(it) }
|
||||
}
|
||||
val data = builder.finish()
|
||||
val vec = getRoot(data).toVector()
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertEquals(5, vec.size)
|
||||
val stringAry = vec.map { it.toString() }.toTypedArray()
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertArrayEquals(ary, stringAry)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBlobArray() {
|
||||
val ary = ByteArray(1000) { Random.nextInt().toByte() }
|
||||
val builder = FlexBuffersBuilder()
|
||||
builder.put(ary)
|
||||
val data = builder.finish()
|
||||
val blob = getRoot(data).toBlob()
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertArrayEquals(ary, blob.toByteArray())
|
||||
for (i in 0 until blob.size) {
|
||||
assertEquals(ary[i], blob[i])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArrays() {
|
||||
val builder = FlexBuffersBuilder()
|
||||
val ary: Array<String> = Array(5) { "Hello world number: $it" }
|
||||
val numbers = IntArray(10) { it }
|
||||
val doubles = DoubleArray(10) { it * 0.35 }
|
||||
|
||||
// add 3 level array of arrays in the following way
|
||||
// [ [ "..", ...] [ "..", ..., [ "..", ...] ] ]
|
||||
val vec = builder.startVector()
|
||||
|
||||
// [0, 1, 2, 3 ,4 ,5 ,6 ,7 ,8, 9]
|
||||
val vec1 = builder.startVector()
|
||||
numbers.forEach { builder.put(it) }
|
||||
builder.endTypedVector(vec1)
|
||||
|
||||
// [0, 2, 4, 6 , 8, 10, 12, 14, 16, 18]
|
||||
builder.putTypedVector { doubles.forEach { put(it) } }
|
||||
|
||||
// nested array
|
||||
// [ "He..", "He..", "He..", "He..", "He..", [ "He..", "He..", "He..", "He..", "He.." ] ]
|
||||
val vec3 = builder.startVector()
|
||||
ary.forEach { builder.put(it) }
|
||||
builder.putVector { ary.forEach { put("inner: $it") } }
|
||||
builder.endVector(vec3)
|
||||
|
||||
builder.endVector(vec)
|
||||
|
||||
val data = builder.finish()
|
||||
val ref = getRoot(data)
|
||||
val vecRef = getRoot(data).toVector()
|
||||
// although we put a long, it is shrink to a byte
|
||||
assertEquals(3, vecRef.size)
|
||||
|
||||
assertArrayEquals(numbers, vecRef[0].toVector().map { it.toInt() }.toIntArray())
|
||||
assertArrayEquals(doubles, ref[1].toDoubleArray())
|
||||
assertEquals("Hello world number: 4", vecRef[2][4].toString())
|
||||
assertEquals("inner: Hello world number: 4", vecRef[2][5][4].toString())
|
||||
assertEquals("inner: Hello world number: 4", ref[2][5][4].toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMap() {
|
||||
val builder = FlexBuffersBuilder(shareFlag = FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
|
||||
builder.putVector {
|
||||
put(10)
|
||||
putMap {
|
||||
this["chello"] = "world"
|
||||
this["aint"] = 10
|
||||
this["bfloat"] = 12.3
|
||||
}
|
||||
put("aString")
|
||||
}
|
||||
|
||||
val ref = getRoot(builder.finish())
|
||||
val map = ref.toVector()
|
||||
assertEquals(3, map.size)
|
||||
assertEquals(10, map[0].toInt())
|
||||
assertEquals("aString", map[2].toString())
|
||||
assertEquals("world", map[1]["chello"].toString())
|
||||
assertEquals(10, map[1]["aint"].toInt())
|
||||
assertEquals(12.3, map[1]["bfloat"].toDouble())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultiMap() {
|
||||
val builder = FlexBuffersBuilder(shareFlag = FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
|
||||
builder.putMap {
|
||||
this["hello"] = "world"
|
||||
this["int"] = 10
|
||||
this["float"] = 12.3
|
||||
this["intarray"] = intArrayOf(1, 2, 3, 4, 5)
|
||||
this.putMap("myMap") {
|
||||
this["cool"] = "beans"
|
||||
}
|
||||
}
|
||||
|
||||
val ref = getRoot(builder.finish())
|
||||
val map = ref.toMap()
|
||||
assertEquals(5, map.size)
|
||||
assertEquals("world", map["hello"].toString())
|
||||
assertEquals(10, map["int"].toInt())
|
||||
assertEquals(12.3, map["float"].toDouble())
|
||||
assertArrayEquals(intArrayOf(1, 2, 3, 4, 5), map["intarray"].toIntArray())
|
||||
assertEquals("beans", ref["myMap"]["cool"].toString())
|
||||
assertEquals(true, "myMap" in map)
|
||||
assertEquals(true, "cool" in map["myMap"].toMap())
|
||||
|
||||
// testing null values
|
||||
assertEquals(true, ref["invalid_key"].isNull)
|
||||
|
||||
val keys = map.keys.toTypedArray()
|
||||
arrayOf("hello", "int", "float", "intarray", "myMap").sortedArray().forEachIndexed { i: Int, it: String ->
|
||||
assertEquals(it, keys[i].toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBigStringMap() {
|
||||
val builder = FlexBuffersBuilder(shareFlag = FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
|
||||
|
||||
val stringKey = Array(10000) { "Ḧ̵̘́ȩ̵̐myFairlyBigKey$it" }
|
||||
val stringValue = Array(10000) { "Ḧ̵̘́ȩ̵̐myFairlyBigValue$it" }
|
||||
val hashMap = mutableMapOf<String, String>()
|
||||
val pos = builder.startMap()
|
||||
for (i in stringKey.indices) {
|
||||
builder[stringKey[i]] = stringValue[i]
|
||||
hashMap[stringKey[i]] = stringValue[i]
|
||||
}
|
||||
builder.endMap(pos)
|
||||
val ref = getRoot(builder.finish())
|
||||
val map = ref.toMap()
|
||||
val sortedKeys = stringKey.sortedArray()
|
||||
val size = map.size
|
||||
for (i in 0 until size) {
|
||||
assertEquals(sortedKeys[i], map.keyAsString(i))
|
||||
assertEquals(sortedKeys[i], map.keyAt(i).toString())
|
||||
assertEquals(hashMap[sortedKeys[i]], map[map.keyAt(i)].toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKeysAccess() {
|
||||
for (i in 1 until 1000) {
|
||||
val utf8String = "ሰማይ አይታረስ ንጉሥ አይከሰስ።$i"
|
||||
val bytes = ByteArray(Utf8.encodedLength(utf8String))
|
||||
val pos = Utf8.encodeUtf8Array(utf8String, bytes)
|
||||
val key = Key(ArrayReadWriteBuffer(bytes), 0, pos)
|
||||
assertEquals(utf8String.length, key.sizeInChars)
|
||||
for (j in utf8String.indices) {
|
||||
assertEquals(utf8String[j], key[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+427
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class JSONTest {
|
||||
|
||||
@Test
|
||||
fun parse2Test() {
|
||||
val dataStr = """
|
||||
{ "myKey" : [1, "yay"] }
|
||||
""".trimIndent()
|
||||
val data = dataStr.encodeToByteArray()
|
||||
val buffer = ArrayReadWriteBuffer(data, writePosition = data.size)
|
||||
val parser = JSONParser()
|
||||
val root = parser.parse(buffer)
|
||||
println(root.toJson())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseSample() {
|
||||
val dataStr = """
|
||||
{
|
||||
"ary" : [1, 2, 3],
|
||||
"boolean_false": false,
|
||||
"boolean_true": true, "double": 1.2E33,
|
||||
"hello":"world"
|
||||
,"interesting": "value",
|
||||
|
||||
"null_value": null,
|
||||
|
||||
|
||||
"object" : {
|
||||
"field1": "hello"
|
||||
}
|
||||
}
|
||||
"""
|
||||
val data = dataStr.encodeToByteArray()
|
||||
val root = JSONParser().parse(ArrayReadWriteBuffer(data, writePosition = data.size))
|
||||
println(root.toJson())
|
||||
val map = root.toMap()
|
||||
|
||||
assertEquals(8, map.size)
|
||||
assertEquals("world", map["hello"].toString())
|
||||
assertEquals("value", map["interesting"].toString())
|
||||
assertEquals(12e32, map["double"].toDouble())
|
||||
assertArrayEquals(intArrayOf(1, 2, 3), map["ary"].toIntArray())
|
||||
assertEquals(true, map["boolean_true"].toBoolean())
|
||||
assertEquals(false, map["boolean_false"].toBoolean())
|
||||
assertEquals(true, map["null_value"].isNull)
|
||||
assertEquals("hello", map["object"]["field1"].toString())
|
||||
|
||||
val obj = map["object"]
|
||||
assertEquals(true, obj.isMap)
|
||||
assertEquals("{\"field1\":\"hello\"}", obj.toJson())
|
||||
// TODO: Kotlin Double.toString() produce different strings dependending on the platform, so on JVM
|
||||
// is 1.2E33, while on js is 1.2e+33. For now we are disabling this test.
|
||||
//
|
||||
// val minified = data.filterNot { it == ' '.toByte() || it == '\n'.toByte() }.toByteArray().decodeToString()
|
||||
// assertEquals(minified, root.toJson())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoubles() {
|
||||
val values = arrayOf(
|
||||
"-0.0",
|
||||
"1.0",
|
||||
"1.7976931348613157",
|
||||
"0.0",
|
||||
"-0.5",
|
||||
"3.141592653589793",
|
||||
"2.718281828459045E-3",
|
||||
"2.2250738585072014E-308",
|
||||
"4.9E-15",
|
||||
)
|
||||
val parser = JSONParser()
|
||||
assertEquals(-0.0, parser.parse(values[0]).toDouble())
|
||||
assertEquals(1.0, parser.parse(values[1]).toDouble())
|
||||
assertEquals(1.7976931348613157, parser.parse(values[2]).toDouble())
|
||||
assertEquals(0.0, parser.parse(values[3]).toDouble())
|
||||
assertEquals(-0.5, parser.parse(values[4]).toDouble())
|
||||
assertEquals(3.141592653589793, parser.parse(values[5]).toDouble())
|
||||
assertEquals(2.718281828459045e-3, parser.parse(values[6]).toDouble())
|
||||
assertEquals(2.2250738585072014E-308, parser.parse(values[7]).toDouble())
|
||||
assertEquals(4.9E-15, parser.parse(values[8]).toDouble())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInts() {
|
||||
val values = arrayOf(
|
||||
"-0",
|
||||
"0",
|
||||
"-1",
|
||||
"${Int.MAX_VALUE}",
|
||||
"${Int.MIN_VALUE}",
|
||||
"${Long.MAX_VALUE}",
|
||||
"${Long.MIN_VALUE}",
|
||||
)
|
||||
val parser = JSONParser()
|
||||
|
||||
assertEquals(parser.parse(values[0]).toInt(), 0)
|
||||
assertEquals(parser.parse(values[1]).toInt(), 0)
|
||||
assertEquals(parser.parse(values[2]).toInt(), -1)
|
||||
assertEquals(parser.parse(values[3]).toInt(), Int.MAX_VALUE)
|
||||
assertEquals(parser.parse(values[4]).toInt(), Int.MIN_VALUE)
|
||||
assertEquals(parser.parse(values[5]).toLong(), Long.MAX_VALUE)
|
||||
assertEquals(parser.parse(values[6]).toLong(), Long.MIN_VALUE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBooleansAndNull() {
|
||||
val values = arrayOf(
|
||||
"true",
|
||||
"false",
|
||||
"null"
|
||||
)
|
||||
val parser = JSONParser()
|
||||
|
||||
assertEquals(true, parser.parse(values[0]).toBoolean())
|
||||
assertEquals(false, parser.parse(values[1]).toBoolean())
|
||||
assertEquals(true, parser.parse(values[2]).isNull)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStrings() {
|
||||
val values = arrayOf(
|
||||
"\"\"",
|
||||
"\"a\"",
|
||||
"\"hello world\"",
|
||||
"\"\\\"\\\\\\/\\b\\f\\n\\r\\t cool\"",
|
||||
"\"\\u0000\"",
|
||||
"\"\\u0021\"",
|
||||
"\"hell\\u24AC\\n\\ro wor \\u0021 ld\"",
|
||||
"\"\\/_\\\\_\\\"_\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\n\\r\\t`1~!@#\$%^&*()_+-=[]{}|;:',./<>?\"",
|
||||
)
|
||||
val parser = JSONParser()
|
||||
|
||||
// empty
|
||||
var ref = parser.parse(values[0])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals("", ref.toString())
|
||||
// a
|
||||
ref = parser.parse(values[1])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals("a", ref.toString())
|
||||
// hello world
|
||||
ref = parser.parse(values[2])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals("hello world", ref.toString())
|
||||
// "\\\"\\\\\\/\\b\\f\\n\\r\\t\""
|
||||
ref = parser.parse(values[3])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals("\"\\/\b${12.toChar()}\n\r\t cool", ref.toString())
|
||||
// 0
|
||||
ref = parser.parse(values[4])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals(0.toChar().toString(), ref.toString())
|
||||
// u0021
|
||||
ref = parser.parse(values[5])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals(0x21.toChar().toString(), ref.toString())
|
||||
// "\"hell\\u24AC\\n\\ro wor \\u0021 ld\"",
|
||||
ref = parser.parse(values[6])
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals("hell${0x24AC.toChar()}\n\ro wor ${0x21.toChar()} ld", ref.toString())
|
||||
|
||||
ref = parser.parse(values[7])
|
||||
println(ref.toJson())
|
||||
assertEquals(true, ref.isString)
|
||||
assertEquals("/_\\_\"_쫾몾ꮘﳞ볚\b\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?", ref.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnicode() {
|
||||
// took from test/unicode_test.json
|
||||
val data = """
|
||||
{
|
||||
"name": "unicode_test",
|
||||
"testarrayofstring": [
|
||||
"Цлїςσδε",
|
||||
"フムアムカモケモ",
|
||||
"フムヤムカモケモ",
|
||||
"㊀㊁㊂㊃㊄",
|
||||
"☳☶☲",
|
||||
"𡇙𝌆"
|
||||
],
|
||||
"testarrayoftables": [
|
||||
{
|
||||
"name": "Цлїςσδε"
|
||||
},
|
||||
{
|
||||
"name": "☳☶☲"
|
||||
},
|
||||
{
|
||||
"name": "フムヤムカモケモ"
|
||||
},
|
||||
{
|
||||
"name": "㊀㊁㊂㊃㊄"
|
||||
},
|
||||
{
|
||||
"name": "フムアムカモケモ"
|
||||
},
|
||||
{
|
||||
"name": "𡇙𝌆"
|
||||
}
|
||||
]
|
||||
}
|
||||
""".trimIndent()
|
||||
val parser = JSONParser()
|
||||
val ref = parser.parse(data)
|
||||
|
||||
// name
|
||||
assertEquals(3, ref.toMap().size)
|
||||
assertEquals("unicode_test", ref["name"].toString())
|
||||
// testarrayofstring
|
||||
assertEquals(6, ref["testarrayofstring"].toVector().size)
|
||||
assertEquals("Цлїςσδε", ref["testarrayofstring"][0].toString())
|
||||
assertEquals("フムアムカモケモ", ref["testarrayofstring"][1].toString())
|
||||
assertEquals("フムヤムカモケモ", ref["testarrayofstring"][2].toString())
|
||||
assertEquals("㊀㊁㊂㊃㊄", ref["testarrayofstring"][3].toString())
|
||||
assertEquals("☳☶☲", ref["testarrayofstring"][4].toString())
|
||||
assertEquals("𡇙𝌆", ref["testarrayofstring"][5].toString())
|
||||
// testarrayoftables
|
||||
assertEquals(6, ref["testarrayoftables"].toVector().size)
|
||||
assertEquals("Цлїςσδε", ref["testarrayoftables"][0]["name"].toString())
|
||||
assertEquals("☳☶☲", ref["testarrayoftables"][1]["name"].toString())
|
||||
assertEquals("フムヤムカモケモ", ref["testarrayoftables"][2]["name"].toString())
|
||||
assertEquals("㊀㊁㊂㊃㊄", ref["testarrayoftables"][3]["name"].toString())
|
||||
assertEquals("フムアムカモケモ", ref["testarrayoftables"][4]["name"].toString())
|
||||
assertEquals("𡇙𝌆", ref["testarrayoftables"][5]["name"].toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArrays() {
|
||||
val values = arrayOf(
|
||||
"[]",
|
||||
"[1]",
|
||||
"[0,1, 2,3 , 4 ]",
|
||||
"[1.0, 2.2250738585072014E-308, 4.9E-320]",
|
||||
"[1.0, 2, \"hello world\"] ",
|
||||
"[ 1.1, 2, [ \"hello\" ] ]",
|
||||
"[[[1]]]"
|
||||
)
|
||||
val parser = JSONParser()
|
||||
|
||||
// empty
|
||||
var ref = parser.parse(values[0])
|
||||
assertEquals(true, ref.isVector)
|
||||
assertEquals(0, parser.parse(values[0]).toVector().size)
|
||||
// single
|
||||
ref = parser.parse(values[1])
|
||||
assertEquals(true, ref.isTypedVector)
|
||||
assertEquals(1, ref[0].toInt())
|
||||
// ints
|
||||
ref = parser.parse(values[2])
|
||||
assertEquals(true, ref.isTypedVector)
|
||||
assertEquals(T_VECTOR_INT, ref.type)
|
||||
assertEquals(5, ref.toVector().size)
|
||||
for (i in 0..4) {
|
||||
assertEquals(i, ref[i].toInt())
|
||||
}
|
||||
// floats
|
||||
ref = parser.parse(values[3])
|
||||
assertEquals(true, ref.isTypedVector)
|
||||
assertEquals(T_VECTOR_FLOAT, ref.type)
|
||||
assertEquals(3, ref.toVector().size)
|
||||
assertEquals(1.0, ref[0].toDouble())
|
||||
assertEquals(2.2250738585072014E-308, ref[1].toDouble())
|
||||
assertEquals(4.9E-320, ref[2].toDouble())
|
||||
// mixed
|
||||
ref = parser.parse(values[4])
|
||||
assertEquals(false, ref.isTypedVector)
|
||||
assertEquals(T_VECTOR, ref.type)
|
||||
assertEquals(1.0, ref[0].toDouble())
|
||||
assertEquals(2, ref[1].toInt())
|
||||
assertEquals("hello world", ref[2].toString())
|
||||
// nester array
|
||||
ref = parser.parse(values[5])
|
||||
assertEquals(false, ref.isTypedVector)
|
||||
assertEquals(T_VECTOR, ref.type)
|
||||
assertEquals(1.1, ref[0].toDouble())
|
||||
assertEquals(2, ref[1].toInt())
|
||||
assertEquals("hello", ref[2][0].toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Several test cases provided by json.org
|
||||
* For more details, see: http://json.org/JSON_checker/, with only
|
||||
* one exception. Single strings are considered accepted, whereas on
|
||||
* the test suit is should fail.
|
||||
*/
|
||||
@Test
|
||||
fun testParseMustFail() {
|
||||
val failList = listOf(
|
||||
"[\"Unclosed array\"",
|
||||
"{unquoted_key: \"keys must be quoted\"}",
|
||||
"[\"extra comma\",]",
|
||||
"[\"double extra comma\",,]",
|
||||
"[ , \"<-- missing value\"]",
|
||||
"[\"Comma after the close\"],",
|
||||
"[\"Extra close\"]]",
|
||||
"{\"Extra comma\": true,}",
|
||||
"{\"Extra value after close\": true} \"misplaced quoted value\"",
|
||||
"{\"Illegal expression\": 1 + 2}",
|
||||
"{\"Illegal invocation\": alert()}",
|
||||
"{\"Numbers cannot have leading zeroes\": 013}",
|
||||
"{\"Numbers cannot be hex\": 0x14}",
|
||||
"[\"Illegal backslash escape: \\x15\"]",
|
||||
"[\\naked]",
|
||||
"[\"Illegal backslash escape: \\017\"]",
|
||||
"[[[[[[[[[[[[[[[[[[[[[[[\"Too deep\"]]]]]]]]]]]]]]]]]]]]]]]",
|
||||
"{\"Missing colon\" null}",
|
||||
"{\"Double colon\":: null}",
|
||||
"{\"Comma instead of colon\", null}",
|
||||
"[\"Colon instead of comma\": false]",
|
||||
"[\"Bad value\", truth]",
|
||||
"['single quote']",
|
||||
"[\"\ttab\tcharacter\tin\tstring\t\"]",
|
||||
"[\"tab\\ character\\ in\\ string\\ \"]",
|
||||
"[\"line\nbreak\"]",
|
||||
"[\"line\\\nbreak\"]",
|
||||
"[0e]",
|
||||
"[0e+]",
|
||||
"[0e+-1]",
|
||||
"{\"Comma instead if closing brace\": true,",
|
||||
"[\"mismatch\"}"
|
||||
)
|
||||
for (data in failList) {
|
||||
try {
|
||||
JSONParser().parse(ArrayReadBuffer(data.encodeToByteArray()))
|
||||
assertTrue(false, "SHOULD NOT PASS: $data")
|
||||
} catch (e: IllegalStateException) {
|
||||
println("FAIL $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseMustPass() {
|
||||
val passList = listOf(
|
||||
"[\n" +
|
||||
" \"JSON Test Pattern pass1\",\n" +
|
||||
" {\"object with 1 member\":[\"array with 1 element\"]},\n" +
|
||||
" {},\n" +
|
||||
" [],\n" +
|
||||
" -42,\n" +
|
||||
" true,\n" +
|
||||
" false,\n" +
|
||||
" null,\n" +
|
||||
" {\n" +
|
||||
" \"integer\": 1234567890,\n" +
|
||||
" \"real\": -9876.543210,\n" +
|
||||
" \"e\": 0.123456789e-12,\n" +
|
||||
" \"E\": 1.234567890E+34,\n" +
|
||||
" \"\": 23456789012E66,\n" +
|
||||
" \"zero\": 0,\n" +
|
||||
" \"one\": 1,\n" +
|
||||
" \"space\": \" \",\n" +
|
||||
" \"quote\": \"\\\"\",\n" +
|
||||
" \"backslash\": \"\\\\\",\n" +
|
||||
" \"controls\": \"\\b\\f\\n\\r\\t\",\n" +
|
||||
" \"slash\": \"/ & \\/\",\n" +
|
||||
" \"alpha\": \"abcdefghijklmnopqrstuvwyz\",\n" +
|
||||
" \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\",\n" +
|
||||
" \"digit\": \"0123456789\",\n" +
|
||||
" \"0123456789\": \"digit\",\n" +
|
||||
" \"special\": \"`1~!@#\$%^&*()_+-={':[,]}|;.</>?\",\n" +
|
||||
" \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\",\n" +
|
||||
" \"true\": true,\n" +
|
||||
" \"false\": false,\n" +
|
||||
" \"null\": null,\n" +
|
||||
" \"array\":[ ],\n" +
|
||||
" \"object\":{ },\n" +
|
||||
" \"address\": \"50 St. James Street\",\n" +
|
||||
" \"url\": \"http://www.JSON.org/\",\n" +
|
||||
" \"comment\": \"// /* <!-- --\",\n" +
|
||||
" \"# -- --> */\": \" \",\n" +
|
||||
" \" s p a c e d \" :[1,2 , 3\n" +
|
||||
"\n" +
|
||||
",\n" +
|
||||
"\n" +
|
||||
"4 , 5 , 6 ,7 ],\"compact\":[1,2,3,4,5,6,7],\n" +
|
||||
" \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\",\n" +
|
||||
" \"quotes\": \"" \\u0022 %22 0x22 034 "\",\n" +
|
||||
" \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#\$%^&*()_+-=[]{}|;:',./<>?\"\n" +
|
||||
": \"A key can be any string\"\n" +
|
||||
" },\n" +
|
||||
" 0.5 ,98.6\n" +
|
||||
",\n" +
|
||||
"99.44\n" +
|
||||
",\n" +
|
||||
"\n" +
|
||||
"1066,\n" +
|
||||
"1e1,\n" +
|
||||
"0.1e1,\n" +
|
||||
"1e-1,\n" +
|
||||
"1e00,2e+00,2e-00\n" +
|
||||
",\"rosebud\"]",
|
||||
"{\n" +
|
||||
" \"JSON Test Pattern pass3\": {\n" +
|
||||
" \"The outermost value\": \"must be an object or array.\",\n" +
|
||||
" \"In this test\": \"It is an object.\"\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
"[[[[[[[[[[[[[[[[[[[\"Not too deep\"]]]]]]]]]]]]]]]]]]]",
|
||||
)
|
||||
for (data in passList) {
|
||||
JSONParser().parse(ArrayReadBuffer(data.encodeToByteArray()))
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
/**
|
||||
* This implementation uses Little Endian order.
|
||||
*/
|
||||
public actual inline fun ByteArray.getUByte(index: Int): UByte = ByteArrayOps.getUByte(this, index)
|
||||
public actual inline fun ByteArray.getShort(index: Int): Short = ByteArrayOps.getShort(this, index)
|
||||
public actual inline fun ByteArray.getUShort(index: Int): UShort = ByteArrayOps.getUShort(this, index)
|
||||
public actual inline fun ByteArray.getInt(index: Int): Int = ByteArrayOps.getInt(this, index)
|
||||
public actual inline fun ByteArray.getUInt(index: Int): UInt = ByteArrayOps.getUInt(this, index)
|
||||
public actual inline fun ByteArray.getLong(index: Int): Long = ByteArrayOps.getLong(this, index)
|
||||
public actual inline fun ByteArray.getULong(index: Int): ULong = ByteArrayOps.getULong(this, index)
|
||||
public actual inline fun ByteArray.getFloat(index: Int): Float = ByteArrayOps.getFloat(this, index)
|
||||
public actual inline fun ByteArray.getDouble(index: Int): Double = ByteArrayOps.getDouble(this, index)
|
||||
|
||||
public actual inline fun ByteArray.setUByte(index: Int, value: UByte): Unit = ByteArrayOps.setUByte(this, index, value)
|
||||
public actual inline fun ByteArray.setShort(index: Int, value: Short): Unit = ByteArrayOps.setShort(this, index, value)
|
||||
public actual inline fun ByteArray.setUShort(index: Int, value: UShort): Unit = ByteArrayOps.setUShort(this, index, value)
|
||||
public actual inline fun ByteArray.setInt(index: Int, value: Int): Unit = ByteArrayOps.setInt(this, index, value)
|
||||
public actual inline fun ByteArray.setUInt(index: Int, value: UInt): Unit = ByteArrayOps.setUInt(this, index, value)
|
||||
public actual inline fun ByteArray.setLong(index: Int, value: Long): Unit = ByteArrayOps.setLong(this, index, value)
|
||||
public actual inline fun ByteArray.setULong(index: Int, value: ULong): Unit = ByteArrayOps.setULong(this, index, value)
|
||||
public actual inline fun ByteArray.setFloat(index: Int, value: Float): Unit = ByteArrayOps.setFloat(this, index, value)
|
||||
public actual inline fun ByteArray.setDouble(index: Int, value: Double): Unit = ByteArrayOps.setDouble(this, index, value)
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
@file:JvmName("JVMByteArray")
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
/**
|
||||
* This implementation uses Little Endian order.
|
||||
*/
|
||||
public actual inline fun ByteArray.getUByte(index: Int): UByte = ByteArrayOps.getUByte(this, index)
|
||||
public actual inline fun ByteArray.getShort(index: Int): Short = ByteArrayOps.getShort(this, index)
|
||||
public actual inline fun ByteArray.getUShort(index: Int): UShort = ByteArrayOps.getUShort(this, index)
|
||||
public actual inline fun ByteArray.getInt(index: Int): Int = ByteArrayOps.getInt(this, index)
|
||||
public actual inline fun ByteArray.getUInt(index: Int): UInt = ByteArrayOps.getUInt(this, index)
|
||||
public actual inline fun ByteArray.getLong(index: Int): Long = ByteArrayOps.getLong(this, index)
|
||||
public actual inline fun ByteArray.getULong(index: Int): ULong = ByteArrayOps.getULong(this, index)
|
||||
public actual inline fun ByteArray.getFloat(index: Int): Float = ByteArrayOps.getFloat(this, index)
|
||||
public actual inline fun ByteArray.getDouble(index: Int): Double = ByteArrayOps.getDouble(this, index)
|
||||
|
||||
public actual inline fun ByteArray.setUByte(index: Int, value: UByte): Unit = ByteArrayOps.setUByte(this, index, value)
|
||||
public actual inline fun ByteArray.setShort(index: Int, value: Short): Unit = ByteArrayOps.setShort(this, index, value)
|
||||
public actual inline fun ByteArray.setUShort(index: Int, value: UShort): Unit = ByteArrayOps.setUShort(this, index, value)
|
||||
public actual inline fun ByteArray.setInt(index: Int, value: Int): Unit = ByteArrayOps.setInt(this, index, value)
|
||||
public actual inline fun ByteArray.setUInt(index: Int, value: UInt): Unit = ByteArrayOps.setUInt(this, index, value)
|
||||
public actual inline fun ByteArray.setLong(index: Int, value: Long): Unit = ByteArrayOps.setLong(this, index, value)
|
||||
public actual inline fun ByteArray.setULong(index: Int, value: ULong): Unit = ByteArrayOps.setULong(this, index, value)
|
||||
public actual inline fun ByteArray.setFloat(index: Int, value: Float): Unit = ByteArrayOps.setFloat(this, index, value)
|
||||
public actual inline fun ByteArray.setDouble(index: Int, value: Double): Unit = ByteArrayOps.setDouble(this, index, value)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class Utf8Test {
|
||||
|
||||
@Test
|
||||
fun testUtf8EncodingDecoding() {
|
||||
val classLoader = this.javaClass.classLoader
|
||||
val utf8Lines = String(classLoader.getResourceAsStream("utf8_sample.txt")!!.readBytes())
|
||||
.split("\n")
|
||||
.filter { it.trim().isNotEmpty() }
|
||||
|
||||
val utf8Bytes = utf8Lines.map {
|
||||
s -> ByteArray(Utf8.encodedLength(s)).also {
|
||||
Utf8.encodeUtf8Array(s, it)
|
||||
}
|
||||
}
|
||||
utf8Bytes.indices.forEach {
|
||||
assertArrayEquals(utf8Lines[it].encodeToByteArray(), utf8Bytes[it])
|
||||
assertEquals(utf8Lines[it], Utf8.decodeUtf8Array(utf8Bytes[it]))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2015-08-28 - CC BY 4.0
|
||||
UTF-8 encoded sample plain-text file
|
||||
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
||||
Markus Kuhn [ˈmaʳkʊs kuːn] <mkuhn@acm.org> — 1999-08-20
|
||||
|
||||
The ASCII compatible UTF-8 encoding of ISO 10646 and Unicode
|
||||
plain-text files is defined in RFC 2279 and in ISO 10646-1 Annex R.
|
||||
|
||||
|
||||
Using Unicode/UTF-8, you can write in emails and source code things such as
|
||||
|
||||
Mathematics and Sciences:
|
||||
|
||||
∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),
|
||||
|
||||
ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),
|
||||
|
||||
2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm
|
||||
|
||||
Linguistics and dictionaries:
|
||||
|
||||
ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
|
||||
Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
|
||||
|
||||
APL:
|
||||
|
||||
((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
|
||||
|
||||
Nicer typography in plain text files:
|
||||
|
||||
╔══════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ • ‘single’ and “double” quotes ║
|
||||
║ ║
|
||||
║ • Curly apostrophes: “We’ve been here” ║
|
||||
║ ║
|
||||
║ • Latin-1 apostrophe and accents: '´` ║
|
||||
║ ║
|
||||
║ • ‚deutsche‘ „Anführungszeichen“ ║
|
||||
║ ║
|
||||
║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║
|
||||
║ ║
|
||||
║ • ASCII safety test: 1lI|, 0OD, 8B ║
|
||||
║ ╭─────────╮ ║
|
||||
║ • the euro symbol: │ 14.95 € │ ║
|
||||
║ ╰─────────╯ ║
|
||||
╚══════════════════════════════════════════╝
|
||||
|
||||
Greek (in Polytonic):
|
||||
|
||||
The Greek anthem:
|
||||
|
||||
Σὲ γνωρίζω ἀπὸ τὴν κόψη
|
||||
τοῦ σπαθιοῦ τὴν τρομερή,
|
||||
σὲ γνωρίζω ἀπὸ τὴν ὄψη
|
||||
ποὺ μὲ βία μετράει τὴ γῆ.
|
||||
|
||||
᾿Απ᾿ τὰ κόκκαλα βγαλμένη
|
||||
τῶν ῾Ελλήνων τὰ ἱερά
|
||||
καὶ σὰν πρῶτα ἀνδρειωμένη
|
||||
χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
|
||||
|
||||
From a speech of Demosthenes in the 4th century BC:
|
||||
|
||||
Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
|
||||
ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
|
||||
λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
|
||||
τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
|
||||
εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
|
||||
πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
|
||||
οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
|
||||
οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
|
||||
ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
|
||||
τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
|
||||
γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
|
||||
προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
|
||||
σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
|
||||
τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
|
||||
τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
|
||||
τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
|
||||
|
||||
Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
|
||||
|
||||
Georgian:
|
||||
|
||||
From a Unicode conference invitation:
|
||||
|
||||
გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
|
||||
კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
|
||||
ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
|
||||
ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
|
||||
ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
|
||||
ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
|
||||
ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
|
||||
|
||||
Russian:
|
||||
|
||||
From a Unicode conference invitation:
|
||||
|
||||
Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
|
||||
Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
|
||||
Конференция соберет широкий круг экспертов по вопросам глобального
|
||||
Интернета и Unicode, локализации и интернационализации, воплощению и
|
||||
применению Unicode в различных операционных системах и программных
|
||||
приложениях, шрифтах, верстке и многоязычных компьютерных системах.
|
||||
|
||||
Thai (UCS Level 2):
|
||||
|
||||
Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
|
||||
classic 'San Gua'):
|
||||
|
||||
[----------------------------|------------------------]
|
||||
๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่
|
||||
สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา
|
||||
ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา
|
||||
โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ
|
||||
เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ
|
||||
ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
|
||||
พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้
|
||||
ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
|
||||
|
||||
(The above is a two-column text. If combining characters are handled
|
||||
correctly, the lines of the second column should be aligned with the
|
||||
| character above.)
|
||||
|
||||
Ethiopian:
|
||||
|
||||
Proverbs in the Amharic language:
|
||||
|
||||
ሰማይ አይታረስ ንጉሥ አይከሰስ።
|
||||
ብላ ካለኝ እንደአባቴ በቆመጠኝ።
|
||||
ጌጥ ያለቤቱ ቁምጥና ነው።
|
||||
ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
|
||||
የአፍ ወለምታ በቅቤ አይታሽም።
|
||||
አይጥ በበላ ዳዋ ተመታ።
|
||||
ሲተረጉሙ ይደረግሙ።
|
||||
ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
|
||||
ድር ቢያብር አንበሳ ያስር።
|
||||
ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
|
||||
እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
|
||||
የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
|
||||
ሥራ ከመፍታት ልጄን ላፋታት።
|
||||
ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
|
||||
የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
|
||||
ተንጋሎ ቢተፉ ተመልሶ ባፉ።
|
||||
ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
|
||||
እግርህን በፍራሽህ ልክ ዘርጋ።
|
||||
|
||||
Runes:
|
||||
|
||||
ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
|
||||
|
||||
(Old English, which transcribed into Latin reads 'He cwaeth that he
|
||||
bude thaem lande northweardum with tha Westsae.' and means 'He said
|
||||
that he lived in the northern land near the Western Sea.')
|
||||
|
||||
Braille:
|
||||
|
||||
⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
|
||||
|
||||
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
|
||||
⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
|
||||
⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
|
||||
⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
|
||||
⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
|
||||
⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
|
||||
|
||||
⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
|
||||
|
||||
⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
|
||||
⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
|
||||
⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
|
||||
⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
|
||||
⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
|
||||
⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
|
||||
⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
|
||||
⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
|
||||
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
|
||||
|
||||
(The first couple of paragraphs of "A Christmas Carol" by Dickens)
|
||||
|
||||
Compact font selection example text:
|
||||
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
|
||||
abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
|
||||
–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
|
||||
∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა
|
||||
|
||||
Greetings in various languages:
|
||||
|
||||
Hello world, Καλημέρα κόσμε, コンニチハ
|
||||
|
||||
Box drawing alignment tests: █
|
||||
▉
|
||||
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
|
||||
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
|
||||
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
|
||||
╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
|
||||
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
|
||||
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
|
||||
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.google.flatbuffers.kotlin
|
||||
|
||||
/**
|
||||
* This implementation assumes that of native macOSX64 the byte order of the implementation is Little Endian.
|
||||
*/
|
||||
|
||||
public actual inline fun ByteArray.getUByte(index: Int): UByte = getUByteAt(index)
|
||||
public actual inline fun ByteArray.getShort(index: Int): Short = getShortAt(index)
|
||||
public actual inline fun ByteArray.getUShort(index: Int): UShort = getUShortAt(index)
|
||||
public actual inline fun ByteArray.getInt(index: Int): Int = getIntAt(index)
|
||||
public actual inline fun ByteArray.getUInt(index: Int): UInt = getUIntAt(index)
|
||||
public actual inline fun ByteArray.getLong(index: Int): Long = getLongAt(index)
|
||||
public actual inline fun ByteArray.getULong(index: Int): ULong = getULongAt(index)
|
||||
|
||||
public actual inline fun ByteArray.setUByte(index: Int, value: UByte): Unit = setUByteAt(index, value)
|
||||
public actual inline fun ByteArray.setShort(index: Int, value: Short): Unit = setShortAt(index, value)
|
||||
public actual inline fun ByteArray.setUShort(index: Int, value: UShort): Unit = setUShortAt(index, value)
|
||||
public actual inline fun ByteArray.setInt(index: Int, value: Int): Unit = setIntAt(index, value)
|
||||
public actual inline fun ByteArray.setUInt(index: Int, value: UInt): Unit = setUIntAt(index, value)
|
||||
public actual inline fun ByteArray.setLong(index: Int, value: Long): Unit = setLongAt(index, value)
|
||||
public actual inline fun ByteArray.setULong(index: Int, value: ULong): Unit = setULongAt(index, value)
|
||||
public actual inline fun ByteArray.setFloat(index: Int, value: Float): Unit = setFloatAt(index, value)
|
||||
public actual inline fun ByteArray.setDouble(index: Int, value: Double): Unit = setDoubleAt(index, value)
|
||||
public actual inline fun ByteArray.getFloat(index: Int): Float = Float.fromBits(getIntAt(index))
|
||||
public actual inline fun ByteArray.getDouble(index: Int): Double = Double.fromBits(getLongAt(index))
|
||||
Reference in New Issue
Block a user