/* * Copyright 2014 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. */ import MyGame.Example.* import com.google.flatbuffers.ByteBufferUtil import com.google.flatbuffers.FlatBufferBuilder import NamespaceA.* import NamespaceA.NamespaceB.* import NamespaceA.NamespaceB.TableInNestedNS import java.io.File import java.io.FileOutputStream import java.io.InputStream import java.io.RandomAccessFile import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.channels.FileChannel import com.google.flatbuffers.Constants.SIZE_PREFIX_LENGTH @kotlin.ExperimentalUnsignedTypes class KotlinTest { companion object { @JvmStatic fun main(args: Array) { // First, let's test reading a FlatBuffer generated by C++ code: // This file was generated from monsterdata_test.json val data = RandomAccessFile(File("monsterdata_test.mon"), "r").use { val temp = ByteArray(it.length().toInt()) it.readFully(temp) temp } // Now test it: val bb = ByteBuffer.wrap(data) TestBuffer(bb) // Second, let's create a FlatBuffer from scratch in Java, and test it also. // We use an initial size of 1 to exercise the reallocation algorithm, // normally a size larger than the typical FlatBuffer you generate would be // better for performance. val fbb = FlatBufferBuilder(1) TestBuilderBasics(fbb, true) TestBuilderBasics(fbb, false) TestExtendedBuffer(fbb.dataBuffer().asReadOnlyBuffer()) TestNamespaceNesting() TestNestedFlatBuffer() TestCreateByteVector() TestCreateUninitializedVector() TestByteBufferFactory() TestSizedInputStream() TestVectorOfUnions() println("FlatBuffers test: completed successfully") } fun TestEnums() { assert(Color.name(Color.Red.toInt()) == "Red") assert(Color.name(Color.Blue.toInt()) == "Blue") assert(Any_.name(Any_.NONE.toInt()) == "NONE") assert(Any_.name(Any_.Monster.toInt()) == "Monster") } fun TestBuffer(bb: ByteBuffer) { assert(Monster.MonsterBufferHasIdentifier(bb) == true) val monster = Monster.getRootAsMonster(bb) assert(monster.hp == 80.toShort()) assert(monster.mana == 150.toShort()) // default assert(monster.name == "MyMonster") // monster.friendly() // can't access, deprecated val pos = monster.pos!! assert(pos.x == 1.0f) assert(pos.y == 2.0f) assert(pos.z == 3.0f) assert(pos.test1 == 3.0) // issue: int != byte assert(pos.test2 == Color.Green) val t = pos.test3!! assert(t.a == 5.toShort()) assert(t.b == 6.toByte()) assert(monster.testType == Any_.Monster) val monster2 = Monster() assert(monster.test(monster2) != null == true) assert(monster2.name == "Fred") assert(monster.inventoryLength == 5) var invsum = 0u for (i in 0 until monster.inventoryLength) invsum += monster.inventory(i) assert(invsum == 10u) // Alternative way of accessing a vector: val ibb = monster.inventoryAsByteBuffer invsum = 0u while (ibb.position() < ibb.limit()) invsum += ibb.get().toUInt() assert(invsum == 10u) val test_0 = monster.test4(0)!! val test_1 = monster.test4(1)!! assert(monster.test4Length == 2) assert(test_0.a + test_0.b + test_1.a + test_1.b == 100) assert(monster.testarrayofstringLength == 2) assert(monster.testarrayofstring(0) == "test1") assert(monster.testarrayofstring(1) == "test2") assert(monster.testbool == true) } // this method checks additional fields not present in the binary buffer read from file // these new tests are performed on top of the regular tests fun TestExtendedBuffer(bb: ByteBuffer) { TestBuffer(bb) val monster = Monster.getRootAsMonster(bb) assert(monster.testhashu32Fnv1 == (1u + Integer.MAX_VALUE.toUInt())) } 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) } fun TestNestedFlatBuffer() { val nestedMonsterName = "NestedMonsterName" val nestedMonsterHp: Short = 600 val nestedMonsterMana: Short = 1024 var fbb1: FlatBufferBuilder? = 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 = fbb1.sizedByteArray() val fbb2 = FlatBufferBuilder(16) val str2 = fbb2.createString("My Monster") val nestedBuffer = Monster.createTestnestedflatbufferVector(fbb2, fbb1Bytes.asUByteArray()) 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.getRootAsMonster(fbb2.dataBuffer()) val nestedMonster = mons.testnestedflatbufferAsMonster!! assert(nestedMonsterMana == nestedMonster.mana) assert(nestedMonsterHp == nestedMonster.hp) assert(nestedMonsterName == nestedMonster.name) } fun TestCreateByteVector() { val fbb = FlatBufferBuilder(16) val str = fbb.createString("MyMonster") val inventory = byteArrayOf(0, 1, 2, 3, 4) val vec = fbb.createByteVector(inventory) Monster.startMonster(fbb) Monster.addInventory(fbb, vec) Monster.addName(fbb, str) val monster1 = Monster.endMonster(fbb) Monster.finishMonsterBuffer(fbb, monster1) val monsterObject = Monster.getRootAsMonster(fbb.dataBuffer()) assert(monsterObject.inventory(1) == inventory[1].toUByte()) assert(monsterObject.inventoryLength == inventory.size) assert(ByteBuffer.wrap(inventory) == monsterObject.inventoryAsByteBuffer) } fun TestCreateUninitializedVector() { val fbb = FlatBufferBuilder(16) val str = fbb.createString("MyMonster") val inventory = byteArrayOf(0, 1, 2, 3, 4) val bb = fbb.createUnintializedVector(1, inventory.size, 1) for (i in inventory) { bb.put(i) } val vec = fbb.endVector() Monster.startMonster(fbb) Monster.addInventory(fbb, vec) Monster.addName(fbb, str) val monster1 = Monster.endMonster(fbb) Monster.finishMonsterBuffer(fbb, monster1) val monsterObject = Monster.getRootAsMonster(fbb.dataBuffer()) assert(monsterObject.inventory(1) == inventory[1].toUByte()) assert(monsterObject.inventoryLength == inventory.size) assert(ByteBuffer.wrap(inventory) == monsterObject.inventoryAsByteBuffer) } fun TestByteBufferFactory() { class MappedByteBufferFactory : FlatBufferBuilder.ByteBufferFactory() { override fun newByteBuffer(capacity: Int): ByteBuffer? { var bb: ByteBuffer? try { bb = RandomAccessFile("javatest.bin", "rw").channel.map( FileChannel.MapMode.READ_WRITE, 0, capacity.toLong() ).order(ByteOrder.LITTLE_ENDIAN) } catch (e: Throwable) { println("FlatBuffers test: couldn't map ByteBuffer to a file") bb = null } return bb } } val fbb = FlatBufferBuilder(1, MappedByteBufferFactory()) TestBuilderBasics(fbb, false) } fun TestSizedInputStream() { // Test on default FlatBufferBuilder that uses HeapByteBuffer val fbb = FlatBufferBuilder(1) TestBuilderBasics(fbb, false) val `in` = fbb.sizedInputStream() val array = fbb.sizedByteArray() var count = 0 var currentVal = 0 while (currentVal != -1 && count < array.size) { try { currentVal = `in`.read() } catch (e: java.io.IOException) { println("FlatBuffers test: couldn't read from InputStream") return } assert(currentVal.toByte() == array[count]) count++ } assert(count == array.size) } fun TestBuilderBasics(fbb: FlatBufferBuilder, sizePrefix: Boolean) { val names = intArrayOf(fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma")) val off = IntArray(3) 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 str = fbb.createString("MyMonster") val inv = Monster.createInventoryVector(fbb, byteArrayOf(0, 1, 2, 3, 4).asUByteArray()) val fred = fbb.createString("Fred") Monster.startMonster(fbb) Monster.addName(fbb, fred) val mon2 = Monster.endMonster(fbb) Monster.startTest4Vector(fbb, 2) Test.createTest(fbb, 10.toShort(), 20.toByte()) Test.createTest(fbb, 30.toShort(), 40.toByte()) val test4 = fbb.endVector() val testArrayOfString = Monster.createTestarrayofstringVector(fbb, intArrayOf(fbb.createString("test1"), fbb.createString("test2"))) 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, str) Monster.addInventory(fbb, inv) Monster.addTestType(fbb, Any_.Monster) Monster.addTest(fbb, mon2) Monster.addTest4(fbb, test4) Monster.addTestarrayofstring(fbb, testArrayOfString) Monster.addTestbool(fbb, true) Monster.addTesthashu32Fnv1(fbb, UInt.MAX_VALUE + 1u) Monster.addTestarrayoftables(fbb, sortMons) val mon = Monster.endMonster(fbb) if (sizePrefix) { Monster.finishSizePrefixedMonsterBuffer(fbb, mon) } else { Monster.finishMonsterBuffer(fbb, mon) } // Write the result to a file for debugging purposes: // Note that the binaries are not necessarily identical, since the JSON // parser may serialize in a slightly different order than the above // Java code. They are functionally equivalent though. try { val filename = "monsterdata_java_wire" + (if (sizePrefix) "_sp" else "") + ".mon" val fc = FileOutputStream(filename).channel fc.write(fbb.dataBuffer().duplicate()) fc.close() } catch (e: java.io.IOException) { println("FlatBuffers test: couldn't write file") return } // Test it: var dataBuffer = fbb.dataBuffer() if (sizePrefix) { assert( ByteBufferUtil.getSizePrefix(dataBuffer) + SIZE_PREFIX_LENGTH == dataBuffer.remaining() ) dataBuffer = ByteBufferUtil.removeSizePrefix(dataBuffer) } TestExtendedBuffer(dataBuffer) // Make sure it also works with read only ByteBuffers. This is slower, // since creating strings incurs an additional copy // (see Table.__string). TestExtendedBuffer(dataBuffer.asReadOnlyBuffer()) TestEnums() //Attempt to mutate Monster fields and check whether the buffer has been mutated properly // revert to original values after testing val monster = Monster.getRootAsMonster(dataBuffer) // mana is optional and does not exist in the buffer so the mutation should fail // the mana field should retain its default value assert(monster.mutateMana(10.toShort()) == false) assert(monster.mana == 150.toShort()) // Accessing a vector of sorted by the key tables assert(monster.testarrayoftables(0)!!.name == "Barney") assert(monster.testarrayoftables(1)!!.name == "Frodo") assert(monster.testarrayoftables(2)!!.name == "Wilma") // Example of searching for a table by the key assert(monster.testarrayoftablesByKey("Frodo")!!.name == "Frodo") assert(monster.testarrayoftablesByKey("Barney")!!.name == "Barney") assert(monster.testarrayoftablesByKey("Wilma")!!.name == "Wilma") // testType is an existing field and mutating it should succeed assert(monster.testType == Any_.Monster) assert(monster.mutateTestType(Any_.NONE) == true) assert(monster.testType == Any_.NONE) assert(monster.mutateTestType(Any_.Monster) == true) assert(monster.testType == Any_.Monster) //mutate the inventory vector assert(monster.mutateInventory(0, 1u) == true) assert(monster.mutateInventory(1, 2u) == true) assert(monster.mutateInventory(2, 3u) == true) assert(monster.mutateInventory(3, 4u) == true) assert(monster.mutateInventory(4, 5u) == true) for (i in 0 until monster.inventoryLength) { assert(monster.inventory(i) == (i.toUByte() + 1u).toUByte()) } //reverse mutation assert(monster.mutateInventory(0, 0u) == true) assert(monster.mutateInventory(1, 1u) == true) assert(monster.mutateInventory(2, 2u) == true) assert(monster.mutateInventory(3, 3u) == true) assert(monster.mutateInventory(4, 4u) == true) // get a struct field and edit one of its fields val pos = monster.pos!! assert(pos.x == 1.0f) pos.mutateX(55.0f) assert(pos.x == 55.0f) pos.mutateX(1.0f) assert(pos.x == 1.0f) } fun TestVectorOfUnions() { val fbb = FlatBufferBuilder() val swordAttackDamage = 1 val characterVector = intArrayOf(Attacker.createAttacker(fbb, swordAttackDamage)) val characterTypeVector = ubyteArrayOf(Character_.MuLan) Movie.finishMovieBuffer( fbb, Movie.createMovie( fbb, 0u, 0, Movie.createCharactersTypeVector(fbb, characterTypeVector), Movie.createCharactersVector(fbb, characterVector) ) ) val movie = Movie.getRootAsMovie(fbb.dataBuffer()) assert(movie.charactersTypeLength == characterTypeVector.size) assert(movie.charactersLength == characterVector.size) assert(movie.charactersType(0) == characterTypeVector[0]) assert((movie.characters(Attacker(), 0) as Attacker).swordAttackDamage == swordAttackDamage) } } }