Squashed 'third_party/flatbuffers/' changes from 338393f85..e5f331db9
e5f331db9 [TS] Add single-file ts codegen & bazel rule for typescript (#7161)
2f84c6038 Apply Namer to Go code gen (#7150)
d64839651 [Lobster] file_identifier support
777e78d8d [Lobster] support unsigned integer reads
4016c549d Apply Namer to Python code gen (#7146)
40827b21b Fix missing 'break' (#7151)
65a10b6e3 Implement a config based name manager and use it in Rust codegen (#7144)
8db2fef3f [TS] Escape keywords in typescript object names (#7137)
9ed132304 Fix 64-bit numeric enum values in typescript (#7135)
1a4c40566 updated npm to 2.0.6
318594e4b prevent name clash (#7133)
3d903302c [Rust] Add length checks to arrays and vectors. (#7130)
c9571d989 Replaced ToDasherCase with ConvertCase (#7131)
3694b830a Use ConvertCase instead of Make{Upper,Lower,Snake} implementations (#7127)
0471fa807 remove stall reference to version
914344ea9 some minor help edits
b40266c56 Use target_compile_features to target C++11 (#7122)
8a9303d46 update proto tests with alaised enum (#7121)
30c4bf47f Trigger fuzzing CI only on relevant changes (#7120)
46ce45601 remove auto generate code from cmakelists (#7119)
433312c55 add the missing checkNullConditionnal code in the "GenObjApi" function when the field is scalar optional (#7114)
9c52ec374 Add deps attribute to flatbuffer_cc_library (#7107)
70e2f49bf fixed string-json -> strict-json typo
5ac0367ed [TS] Fix generation of reserved words in object api (#7106) (#7115)
5d101afb5 Fix minor typo in WhitePaper.md (#7108)
06f4af11b Go optional scalars (#7104)
57e338f81 explicitly use windows-2019 to unblock ci (#7105)
615616cb5 Change Rust generated file defaults (#7101)
3413c3300 Fixed FlexBuffers verifier fuzzer timing out
69f5660a4 Fixed Parser not checking size of union types vector
d5add9fca Fixed FlexBuffers ToString unquoted non-ident keys
15df50eb7 Remove @ExperimentalUnsignedTypes annotation from kotlin code generator. (#7092)
a94132a45 Swift FlatBufferBuilder.sizedByteArray to ByteBuffer.toArray() (#7093)
48befb6be [TS] Refactor away circular ref (#7099)
b30069133 Add reflection support for python (#7026)
faadbc10e Add CreateVector overload to accept array like (#7095)
ed6ae8d32 explicitly defined std::allocator (#7094)
e910bddbc [JS] Add ".js" suffix to all import statements to generate a browser compatible mjs version. (#7086)
c85fb690f Add Parsing Completed function for Rust (#7084)
c1daa6ba0 rust: Bump thiserror version and remove git dependency (#7080)
4c71c7b02 replace framework include of flatbuffers (#7079)
f5664d33f fix go_test implement error (#7012)
39c8a19ce fixed comparator for native_inline (#7076)
826193ff6 skip generating reflection.fbs in generate_scripts (#7077)
fd0d1ed92 update C++ generator to emit scoped enums in vector of unions (#7075)
424988f30 replaced removed xml tag
b2550dbaa removed BSD-3-Clause license and Zlib licenses (#7073)
a2b238960 Fixed default value of bool in struct for Swift (#7072)
619b78437 one last dart omit local (#7070)
68a766154 more omit type annotations for dart local variables (#7069)
7dac9961f omit type annotations for dart local variables (#7067)
c65c389c6 add xmlns to .Net (#7066)
6446dcf3d Updates swift gen licence for grpc code (#7063)
e090d8da1 Fixed long cast to int in flexbufferbuilder.java (#7059)
bc901436d clang format on codebase (#7058)
240be9b5a attempt to remove appveyor (#7056)
bc366a7f9 Provide a short help text and default in error case (#6992)
14b19d446 Use FindPython3 (#7055)
9e6f17b94 remove BIICODE
c3801ad37 Update readme.md
4f3b24db0 Fixes a bug where bools arent being nil when marked optional (#7051)
1d294a31b Implement Serialize on generated rust types (#7022)
dd8fccfb1 Cmake 3.16 de-facto minimum version (#7049)
7089c9ecd Convert flatbuffers_version_string to inline function (#7046)
43203984f [C++] Support C++ object copies and moves (#5988)
5993338ee [ts] Builder incorrectly serializing empty strings (#7047)
19920db39 Include a SizePrefixed..HasIdentifier for c++. (#6871)
dbbaeac85 Use `${PYTHON_EXECUTABLE}` instead of `py` in cmake (#7042)
028f0fde6 'flattest': Add --test_path option (#7041)
87343631b Added support for clang-cl on windows (CMake) (#7038)
1fbfaf5c5 Fix/cmake build grpc (#7028)
4421375bb Add FlatBuffers::FlatBuffers interface, needed for FetchContent_Declare (#7023)
162ad7a37 Fix comment with line orders - Rust should be last. (#7037)
a0a6c3f8c Use actions/checkout@v2, which fixes security vulnerability. (#7036)
a2d38fbb9 Add --warnings-as-errors to flatc compiler. (#7034)
9ef1524d3 Emit include for bfbs-gen-embed (#7031)
0c9de0352 Upgraded GRPC version to 1.42.0 (#7033)
a783bc926 chore: dart 2.0.5 release changes (#6983)
aff818ceb rust: Allow for usage in no_std environment (#6989)
d7b75417f Make flatbuffer builder deterministic (#6993)
a42e89897 Added verifier alignment checking to table fields (#7018)
96cc2f3ee BuildFlatBuffers.cmake: fix arguments not passed properly to flatc (#7013)
3250a1f8d Add initial C# vector of unions support to the documentation. (#6880)
ace4a37f2 [TS/JS] BigInt implementation (#6998)
f28c2b293 Avoid implicit conversion from float to double. (#7003)
96f3cf690 Rearrange #include directives to pass to compilation with a particular DSP toolchain (#7000)
b3edfdbfb Reverting grpc generated file suffix (#6995)
b8aaccee8 Disable parsing of nested_flatbuffers as bytes by default
2dc8ae774 Enable OSS-Fuzz on CI
5b0d49112 Updated FlexBuffers fuzzer
d8b7041d7 Added alignment checking to FlexBuffers verifier
5a0c3366c FlexBuffers verifier additionally checks for nesting
f8148b8da Made FlexBuffers reuse tracker track types
a22b1b626 Prevent shadow with _{{FIELD_NAME}} (#6991)
4264daadd FlexBuffers fuzzer fixes
3cbc120a0 Refractor Flatc Options (#6987)
8dcd2682c [CMake] Add option for disable universal on OSX (#6990)
b78002ff3 Validate C# json/object-api options (#6985)
c555ee8fa Add .NET test to github workflows (#6982)
b92bb0584 [CMake]: Fix version in pkgconfig file (#6986)
956d11569 re-enabled FlexBuffer JSON in fuzzer
e367ca32a Verifier for FlexBuffers (#6977)
705f27f6e [CMake]: Fix python command for mingw environment (#6984)
5fc87f4c4 Enable --gen-onefile in Python (#6953)
11749095a Make idl_parser deterministic (#6976)
d0cede9c9 Bumping to Version 2.0.5 (#6967)
fcc2bee0b Fix typo in variable name (#6973)
e08da4dea remove Appveyor's Vs2010 and 13 builds (#6969)
18538c401 [TS] Fix reserved words as arguments (#6955) (#6956)
e57f4ab2d Update stale bot version and provide away to exempt issues with the not-stale label (#6968)
deed68db5 missing generated files (#6966)
061d61f3f Lua Generator using IR. (#6940)
cffe0c454 FixedTypedVector: add const to ElementType() and size() (#6965)
fadd40e40 Have grpc include file with correct filename-suffix given to flatc (#6954)
e47dc0e46 Removed test/generate_code.{sh|bat} (#6873)
5c5475479 switched to windows-lastest, removed deadcode (#6948)
4f7f6dc30 Restore FlatBufferBuilder::kFileIdentifierLength. (#6960)
0fadaf391 Enable verifier on nested_flatbuffers
4d0e9a870 Turn off nested FlatBuffers/FlexBuffers for the fuzzer
c05f6783a invalid conditional running genrate_code.py (#6952)
a14f4052c rust: remove needless borrow (#6922)
9e4ca857b Vector Downward GetSize optimization (#6925)
a2b99084b Fix flexbuffers clang-analyzer warning (#6947)
85b4effac test: fix undefined order of functio parameters. (#6946)
790f41154 [cmake] Fix getting version info from tags during the build (#6936)
e89de4411 clang-all (#6941)
256ab3798 WIP: Dart release 2.0 (#6927)
a59288a01 Add benchmarks to Linux CI (#6928)
587bbd49a [C++] Fix compile failure on Object API union construction for struct member (#6923)
a9c341545 Fix integer overflow warnings
8aa18b629 fixed cmp0048 error (#6932)
d727579b6 fuzzers: fix typo in file path (#6931)
97a30171c Added Raw C++ benchmarks (#6924)
0989fc5e5 Added Google benchmarks (and gtests) (#6920)
927175ea2 [Java] lookup by byteArray is giving back wrong entry (#6915)
6748c373b Removal of support for FLATBUFFERS_CPP98_STL (#6918)
3fab0c6ee move [[ ]] to calling sites (#6914)
6c8c29155 [C++] Split flatbuffers.h into separate files (#6868)
fd4ff23da Keep methods with struct name and switch them to default (#6879)
f8b69e330 [Python] Verify EnumDef is not generated in GenUnionCreator (#6903)
c0394bb09 Support `--conform` for vector of unions (#6883)
8433eb108 Typo fixes in comments of flatbuffers.h and flexbuffers.h (#6901)
f2f9380c8 [Java] Prevent generation of enum names when underlying type is long (#6781) (#6895)
e672dabfe [C++] Fix memory leak when using grpc (#6896) (#6897)
9a4ca2764 Output errors / warnings on stderr instead of stdout (#6881)
4c7a9c10d Adds JSON encoding to swift (#6874)
38295a187 [Kotlin] Fix missing escape for struct construction statements (#6877)
31bb0b972 add working directory (#6878)
45e5642e9 Default Arguments for Mutators C++ [Updated] (#6872)
a592f4c89 [Cmake] Add a post build command after flatc to run the new generate_code.py script. (#6866)
b9d43a557 dart - add Packable interface (#6846)
2ece9e25b Bumped package.json to 2.0.4
2dd212637 Fix linker errors on (Free|Open)BSD. (#6860)
f63c130c2 Improves documentation, and adding DocC (#6784)
e2b26ee19 Make and generate_code.sh generate same outputs (#6855)
9d686bf43 Include subdirectories in published npm package (#6850)
1d26daff3 Use ArrayBuffer type for JS flexbuffer's toObject (#6851)
e72e18d9f feat(build): compile "universal" libraries and executables for Mac (#6852)
354d97f6d fixed errant itemgroup element in C# test project (#6854)
6f7a57eaa [C#] Using 'global::' as qualifying_start_ within BaseGenerator (#6767)
90baa1444 Dart: binary lists (typed_data) (#6839)
0a3b017f0 Delete IDLOptions::lang (#6841)
47d35f105 BugFix: Optional enum when it is null (#6835)
8fb8c2ce1 [C#] Use @ for keyword escaping (#6834)
1d063d87c [C++] Let builder accept custom-alloc std::vector (#6814)
338331b55 Changes to support binary schema file loading and parsing (flatc) (#6823)
156a30c75 Move Apple-specific build flags to Clang section (#6802)
550d2f904 Issue error if jsonschema has no root type defined #6821 (#6831)
c3a38242e Fix typos in docs (#6829)
74c3d7eba [C++] Add mutable version of LookupByKey and test (#6826)
db6eae5c4 Add html and latex to gitignore (#6819)
0e9d79c35 [Java] Avoid casting ByteBuffer to Buffer (#6785)
b20c4d3aa [C++] Add GetMutableSizePrefixedRoot and generate GetMutableSizePrefixedXxx functions (#6815)
ae145293c flattests_cpp17 doesn't compile with Visual Studio 2017: warning C4100: 'indent': unreferenced formal parameter (#6811)
e3c76a5cd Dart lints (#6808)
4b9123baf fix parser string=null (#6810)
f89e0b1a6 Refactor idl_gen_rust and Rust generated code to use consistent whitespace (#6809)
273f6084e Fix Rust keyword inconsistency (#6794)
4a0879458 [C#] Remove superfluous semicolon in C# structs (#6791)
d6f51ea16 fix ptr in flatbuffers cpp tutorial (#6787)
067dce6e7 [C#] Handle keywords (#6775)
2ca5f0e72 [C#] Respect command line parameter for file name extension and suffix (#6779)
f20d2253a update Java version to 2.0.3
390d438e2 [Kotlin] Fix CI by changing compiler to Java 11 (#6783)
6fb2c90d9 avoiding even more NoSuchMethod exceptions (#6729)
a7b527d94 java: Use maven profile to target Java 8 bytecode (#6764)
5d77820b3 [C++] Using calculated fields_number for field_names size (#6769)
909ce970a [C++] Use UnPackTo instead of UnPack if pointer exists (#6725)
35e2cac6e Store vtables sorted in Rust builder (#6765)
c39fc9dd9 [C++] Use nullptr instead of 0 in generated headers (#6762)
775c6567d [C++] Fix compiler warning -Wredundant-parens in clang (#6761)
97d9527f6 Dart 2.0 release prep (#6759)
e01205466 [C++] Use proper gRPC C++ API when using MemoryBuffer Slice (#6756)
5235133f3 Dart - make ascii optimization optional in StringReader, same as in writeString() (#6758)
c871df770 Dart - change table building to assert() instead of exceptions (#6754)
a6eeeb3b9 Dart - inline small functions to increase performance (#6755)
3dd02144d [C#] Fix field name struct name collision (#6744) (#6757)
e77926f0e [C#] Add Union Constructor Utility for ObjectAPI. (#6105)
ac2348202 Dart - fixup writeString() return type after previous merges (#6753)
8be8a0a71 [C++] Fix union type handling on object api in C++17 (#6745)
12e341e4f Rework how Rust generated files are laid out (#6731)
c36672d80 Dart - optimize writeString for ASCII (#6736)
2bfc8e9f0 Dart - make writeString() argument non-nullable (#6737)
674a9f2aa fix lints in rust tests (#6743)
016e6aa13 Add a split on ':' to build_defs.bzl (#6742)
65700441d Dart - make vTable fixed size (expect the number of fields when creating) (#6735)
dd5bb55ca Dart - make vTable deduplication optional (#6734)
e8423da1b Dart - regenerate example after recent flatc changes (#6733)
0980e39c9 flexbuffers: Add variant of Blob() that takes a key (#6730)
e73fab27d Dart - store empty vectors instead of NULL (#6728)
92ae532e4 Dart - finish/lowFinish/buffer changes (#6712)
7482b25f8 Remove ubuntu1604 from presubmit.yml (#6715)
3f77dc9a0 Dart - unpack() must use eager list reader (#6723)
838c93b84 Dart - fix flex-builder compilation error (#6722)
089f48a4a Dart - make sure added padding is zeroed, same as in C++ (#6716)
bf3470c16 Fix UB in CreateVectorOfStructs in case of an empty vector: avoid calling memcpy in this case (#6726)
8f8196e13 Fix 6348 (#6717)
8ab35b2a5 Dart - add custom allocator support (#6711)
c0ba2870c Fix typo in docu (#6714)
22498cf3a [C++] add make_span for Array<T,N> (#6663)
bd37e67ac [C++] Fix a -Wdeprecated-copy warning. (#6708)
a6ee33557 Dart null safety (#6696)
71d43f3be Make --bfbs-filenames default to location of first schema file. (#6705)
c8db1ca5d Jsonschema add prop comments (#6617)
962751a6e Improve generated comparisons for tables (#6486)
06fd6d640 Replace filenames in reflection with filenames+includes. (#6703)
acce4ac3f Fix incorrect assertion usage. (#6702)
8fd7861b7 Fix warning about deprecated module: imp (#6362)
c58ae9422 Add the file a symbol is declared in to Reflection (#6613)
2cf7bb796 Intermediate Representation docs (#6685)
4e3a66c14 Dart object API (#6682)
d959e2320 dart - change Builder.reset() to reuse an existing buffer (#6661)
a9fb54088 use improved versioning (#6691)
337eb8b8f update docs - structs can't be roots (#6690)
6415ef05d [Python] [Codegen] Fixes nested structs with underscore names (#6686)
f069396d1 [C++] flatc --cpp-field-case-style option to permit camel-case field names in C++ (#6669)
021177af0 Fix snap version formatting (#6683)
e1e9f9373 [C++/grpc] added hiding of unused variables in the generated code (#6677)
15110094e Fix GitHub Actions CI gcc/clang versions
093badb0a Use unoptimized path for ReadUInt64 for win32 build as the optimized path crashes. (#6681)
752c7b576 avoiding more NoSuchMethod exceptions (#6671)
7c3e267e1 [Java] ObjectAPI implementation (#6521) (#6582)
baaffbaed npm update to 2.0.3 and script fix
4cb3f222b [TS] Fix module reference in package.json (#6676)
eabdbda75 [TS] Generate entry point module (#6674)
bec23700f Prepare for Java 2.0.1 release
12f2eedad Update CI to GCC/Clang 11
221eeb231 Fix typo in C++ doc (#6664)
813d3632e avoiding NoSuchMethod exception (#6658)
d84bccb0c Removed most heap allocations in builder (#6662)
b4e67f9bf Dart test fix (#6660)
54c11932f [Java] Flexbuffers - Negative signed object length (#6651)
fbcb3c423 [TS/JS] Updates the grpc (#6654)
8937dcfd7 Updates go lang support to allow other languages to communicate with it (#6653)
512d5a689 Update readme.md to point to security policy
a92cb5dd7 Create Security.md
d151dcbb9 Revert "[C++] Removed most heap allocations in builder (#6620)" (#6659)
f1884c66f Fix gRPC test to use EndVector without specifying the length. (#6646)
72730ecd8 [C++] Removed most heap allocations in builder (#6620)
fe2bc2b0a Added README for Rust Flatbuffers (before publishing v=2.0) (#6652)
4867c9456 Fixed generation of c++ code with flags (#6637)
ef0eb3701 Ran clang-format-all.sh. Removed default --style=file parameter (#6639)
f83ee1af5 [idl_parser] Check structs and enums do not clash in a namespace (#6562)
a5175c513 Implements verifier and code gen for swift (#6373)
04b10f5a3 Deprecate EnumVal.object (#6612)
c121c0345 Fixed Java gRPC version number dependency
8fd10606c Implement Serialize for flexbuffer::Reader (#6635)
a1730fcea [Swift] Updated cocoapods to 2.0.0 (#6634)
a9a295fec More missing version changes
5c01ad387 Dart generated code update
42ca1b914 Swift/Kotlin generated code version updates
6ed780dbd C++/Rust version changes
3412fab8e C#/Java generated code version updates
8a7d013f8 Updated main version numbers to 2.0
170af5978 [Go] Add missing namespace when using Object API (#6633)
c8c16de16 Fix reverse iterators for Vector and Array (#6626)
4525cd9c5 [Lua] manipulate byte array as string (#6624)
47361baf6 [C++] Fix union copy constructor to work with nested structs (#6552) (#6607)
8a582883a Updates swift generated monster sample file (#6616)
a4bb8f0c2 [Lua] Avoid infinite loop when creating empty string (#6614)
82aed82b8 Added support for Lua 5.1, 5.2 and 5.4 (#6606)
60ff76630 [TS] Remove wrong and obsolete NS prefix use (#6604)
a27c7d809 Fixed LuaJIT when not compiled with COMPAT mode (#6605)
363220823 removed unneeded type prefixing (#6601)
6b44c605b Bump Rust to 0.9.0 (#6610)
d3cd78a87 [Lua] Add LuaJIT support (#6584)
8fa3dfdb5 Introduce new_from_vec in Rust (also fix formatting) (#6599)
29379e8e4 fix typo in CppUsage.md (#6595)
14725d6c3 [Lua] GetRootAs can accept strings. Made Luatest Benchmarks optional (#6593)
16836ff95 Add advance feature indicators to reflection (#6546)
c87179e73 Rust Remove SafeSliceAccess for Arrays, and fix miri. (#6592)
c24031c36 Mark endian_scalar as unsafe. (#6588)
4ccc52c7a [swift] Implements union strings (#6589)
b82fe0738 [Rust] Fix small mistyping (#6585)
1e7f6c8c7 [TS] Commit the generated tests code (#6579)
84714b109 chore: ensure bash is used to generate code (#6577)
1045d7dd4 [Lua] Fix EnforceNumberAndPack for bool type (#6576)
0c7777596 [TS] Remove duplicated build-ts ci (#6575)
c43ba1752 [Rust] Specify Minimum Supported Rust Version (#6573)
da3bb64ef [Rust] Add support for fixed size arrays (#6548)
151900ba9 [Kotlin][FlexBuffers] Add support for Kotlin-iOS (#6564)
c012f29f9 silenced clippy warning (#6565)
df2df21ec [Kotlin] Bump kotlinx.benchmark dependency to 0.3.0 (#6560)
408e4db4a [TS] Add Build TS to CI jobs (#6524)
4d2364f34 [Kotlin][FlexBuffers] Add support for Kotlin-JS (#6554)
261cf3b20 Default-empty vectors of enums (#6505)
cd67261bb [CI] fixes buildkite (#6544)
1aa0c2f6a Limit formatter to pull requests only (#6540)
4133a39df Rust structz (#6539)
1c26d2a1a [Kotlin][FlexBuffers] JSON support for Flexbuffers (#6417)
276b1bc34 [grpc] Support latest version of grpc PoC (#6338)
124654ffc fixed packing structs (#6530)
3b7d1e86b [GO] Tries to add go format to the CI (#6518)
78f0c0d1d [C++] #6501 - Problem when mapping a native type multiple times (#6514)
c992eafb5 [fuzzer] Add `monster_debug` target (#6513)
ef8dd7792 [Swift] Removes allman rule (#6519)
69b329fc8 [flexbuffers, json] Parse `nan` and `inf` (#6512)
6543ba529 fixed packing structs in nested buffers (#6509)
0e453ac35 [idl_parser] Add kTokenNumericConstant token (#6432)
e9b4ae69d Remove `flatbuffers.pc` from the repository (#6508)
fc4fffea4 Bump grpc to 1.0.0 (#6507)
b240ab704 Move Traits struct and Create method out of --cpp-static-reflection. (#6503)
9a4f1f434 Disable x64-specific optimizations for ARM64EC ReadInt64 (#6506)
fac64918d Add --cpp-static-reflection to generate_code.bat. (#6502)
a69815f72 [C++17] Add compile-time reflection for fields. (#6324)
4033ff589 fixed invalid TS call and added test files (#6495)
a08357251 disable clippy (#6494)
5319dedb1 [idl_parser, JSON] Disable parsing of JSON for incomplete schemes (#6493)
bd4e0b30a [idl_parser] Track included files by hash (#6434)
bf9061200 fix for noUncheckedIndexedAccess in ts (#6474)
8142fedd1 Working on a python example plus fixing python grpc code (#6456)
c0be1cb7a [rust] Remove debug code (#6475)
8cccdfba5 Revert "[C#] Fix truncated ArraySegment<byte> if elementSize != 1 (#6462)" (#6488)
cbbbaa61b [C#] Fix truncated ArraySegment<byte> if elementSize != 1 (#6462)
ffc2ef77c [CI] Adds Code Generation tests on Github Actions (#6482)
1da6f4f18 [CMake] generate pkg-config flatbuffers.pc file (#6455)
b5da526e6 [Swift] Moves grpc example to grpc/examples (#6479)
3b5365762 [TS] Moves grpc code to examples folder (#6476)
e2f5438ac Fixes grammer (#6477)
5e3613f73 Fix sample_binary.py to use latest EndVector without a size. (#6478)
e6b911d40 updated JS docs to reflect current status (#6436)
0c7ae5816 [Go] Working on a go example plus fixing go grpc code (#6448)
ae603b977 [Swift] adds support for default vectors and strings (#6461)
7f47718b6 Update Building.md (#6473)
54dc09e8a GetUOffsetT must get value by GetUint32 not GetInt32 (#6072)
334c6be49 Fix a typo in Swift codegen (#6470)
4174c10e7 [rust] Genericize flexbuffer reader (#6450)
a20f606c2 [Swift] Renaming protocols (#6469)
a72a20827 Update swift docs (#6460)
86401e078 Default strings and vectors: Parser + Rust support (#6421)
6af37e672 [CMake] Renames BUILD files (#6457)
1b88655b0 [Build, cmake] Add -Werror override option (#6429)
0b15916e5 [C++]Fix extra char generation for byte type during json schema generation (#6276)
60eed0ca6 Updating working code (#6441)
0f83367f5 JSON schema - tailing double quotes for maximum (#6452)
fee095410 [idl_parser] Validate `force_align` on all possible paths (#6430)
6f3e45eca Implement Rust object API defaults (#6444)
815d3e820 Upgrade swift grpc to alpha 24 (#6439)
76e7a0ff5 [fuzzer] Limit parser_fuzzer execution time (#6431)
6d91096a2 This commit contains the initial implementation of Flexbuffers in Kotlin. The code was ported based (#6387)
13d9e3585 Better python generated code naming (#6336)
6effe431b Rust: remove inner attributes (#6410)
efcbdc769 [Rust] Ensure unions are referenced with the correct path (#6422)
e581013e3 Refactor FieldDef to model presense as an enum rather than 2 bools. (#6420)
0984d4328 [c++] Apply NativeName before WrapInNameSpace in idl_gen_cpp.cpp (#6419)
786f69b24 Formats cpp code (#6349)
1da0a2dfa Rust Object API (#6070)
796ed68fa Clarify that (Flat|Flex)Buffers do not deduplicate vector elements (#6415)
7b1ee31d8 Clarify that FlatBuffers unions do not support scalars (#6416)
4aff1198d Explain how FlatBuffers encodes unions (#6414)
ad3a729f9 dart Builder - expose finished buffer size (#6403)
52e217706 Remove invalid claim that Protocol Buffers does not support unions (#6413)
760c65755 [TS/JS] New gen TS code gen (#6302)
75c859e98 [idl_parser] Improve symbols lookup thru parent namespaces (#6407)
91b0958c4 Search for includes in the directory containg the current file (#6371)
8008dde11 Upgrade Rust dependencies (#6406)
c81cf8249 [TS/JS] New gen TS code gen prequel to preserve history (#6404)
8573108bb Unset FieldDef.optional if its key (#6402)
7abe612b5 [fuzzer] Fix the binary schema loading in the monster_fuzzer (#6396)
408cf5802 Fix Rust UB problems (#6393)
39e115fdb Define Vector::value_type for STL compatibility (#6394)
85719669c [fuzzer] Debug the monster_tets.bfbs on clusterfuzz server (#6392)
809fe49c7 Fix up scripts, fix generated enum value for strong mode, regenerate files (#6389)
41253e574 [go] tests/GoTest.sh: Fix flags.Parse location to work on new go SDKs. (#6388)
08d2ce844 fix Dart Builder._writeString() - always write trailing zero byte (#6390)
a15a8d930 fix Dart Builder.reset() - clear vTables (#6386)
83ce29cc2 [C++, JSON] Fix nullptr access when reading a key with a default value. (#6375)
4363c1d2c Fix generated EndVector. (#6385)
1bf1ec027 Implements type promotion for Java enum (#6382)
080097653 Delete label_notify.yml
795408115 Disabled PHP CI (#6381)
46545e627 fixed warnings (#6355)
0168178a1 Fix multiple fbs code generation failure (#6365)
82836a62b [idl_parser] Improve stack overflow protection (#6364)
e7430bbeb [idl_parser] Check the range of explicitly set field's id value (#6363)
24dd85fd2 Generate code to encode and decode nested flatbuffers in Python. (#6354)
57f68e289 [Rust] Shared String (#6367)
44cf2bde1 Updates license date to 2021 (#6378)
be37d4da1 include_prefix support for rust (#6330)
4e79d129c [Swift] Rebuild the way swift handles structs from scratch (#6326)
05192553f Fix typos in usage/comments; Make rust generator respect to --filenam… (#6342)
f2511d7d4 Renaming infinity variables in test.cpp (#6340)
f8b203c9c Add vectorNumElements attribute to Builder for simpler vector creation. (#6328)
8ab7c7e2c [CI] Adds formatter to CI (#6272)
7e0039028 Fix Max CI build path (#6333)
65c415911 Generate nullable properties in C# object-based API for optional scalars. (without -gen-mutable) (#6273)
a9e91116d [Python] Commit some orphan python genfile diffs. (#6325)
80a745d9b Fixed missing ending quotes in labeller (#6327)
9fca5e4f4 Add flatc option to inhibit all warnings #6005 (#6301)
92a806b4e [fuzzer] Rename fuzzing dictionaries for `oss-fuzz` (#6318)
9c9baf6d5 bumprust (#6322)
aafc5dc95 Set default initialSize for Builder to 0 (#6310)
442949bc1 Rust Flatbuffers Verifier (#6269)
9064072e8 Version message should be a "STATUS" to avoid going to stderr. (#6316)
fd4c1b5ff Replace std::string and const char* CreateSharedString with string_view (#6315)
bc7eb8ade [fuzzer] Fix mistakes in the `parser` and `scalar` fuzzers. (#6314)
fc960f367 Add default to offset param of Python generated GetRootAs (#6312)
f437f0f7e [fuzzer] Fix loading of schema in monster_fuzzer (#6308)
7f33cf682 [C++] Switch `flatc` to `--cpp-std c++11` C++ code generator (#6306)
8d9eae9ac [idl_parser] Unify parsing of NaN values read from .fbs and .json files (#6296)
2046bffa4 Moved various language tests from AppVeyor to GitHub Actions (#6300)
git-subtree-dir: third_party/flatbuffers
git-subtree-split: e5f331db998a808f78cf5a4880e6f5d0a321c4d0
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
Change-Id: I37f8aaf007fc86226cfa250169a87807afa64a78
diff --git a/kotlin/benchmark/build.gradle.kts b/kotlin/benchmark/build.gradle.kts
new file mode 100644
index 0000000..2294e4c
--- /dev/null
+++ b/kotlin/benchmark/build.gradle.kts
@@ -0,0 +1,111 @@
+import org.jetbrains.kotlin.ir.backend.js.compile
+
+plugins {
+ kotlin("multiplatform") version "1.4.20"
+ id("org.jetbrains.kotlin.plugin.allopen") version "1.4.20"
+ id("org.jetbrains.kotlinx.benchmark") version "0.3.0"
+ id("io.morethan.jmhreport") version "0.9.0"
+ id("de.undercouch.download") version "4.1.1"
+}
+
+// allOpen plugin is needed for the benchmark annotations.
+// for more infomation, see https://github.com/Kotlin/kotlinx-benchmark#gradle-plugin
+allOpen {
+ annotation("org.openjdk.jmh.annotations.State")
+}
+
+group = "com.google.flatbuffers.jmh"
+version = "2.0.0-SNAPSHOT"
+
+// This plugin generates a static html page with the aggregation
+// of all benchmarks ran. very useful visualization tool.
+jmhReport {
+ val baseFolder = project.file("build/reports/benchmarks/main").absolutePath
+ val lastFolder = project.file(baseFolder).list()?.sortedArray()?.lastOrNull() ?: ""
+ jmhResultPath = "$baseFolder/$lastFolder/jvm.json"
+ jmhReportOutput = "$baseFolder/$lastFolder"
+}
+
+// For now we benchmark on JVM only
+benchmark {
+ configurations {
+ this.getByName("main") {
+ iterations = 5
+ iterationTime = 300
+ iterationTimeUnit = "ms"
+ // uncomment for benchmarking JSON op only
+ // include(".*JsonBenchmark.*")
+ }
+ }
+ targets {
+ register("jvm")
+ }
+}
+
+kotlin {
+ jvm {
+ withJava()
+ compilations.all {
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8.toString()
+ }
+ }
+ }
+
+ sourceSets {
+
+ all {
+ languageSettings.enableLanguageFeature("InlineClasses")
+ languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
+ }
+
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test-common"))
+ implementation(kotlin("test-annotations-common"))
+ }
+ }
+ val jvmTest by getting {
+ dependencies {
+ implementation(kotlin("test-junit"))
+ }
+ }
+ val jvmMain by getting {
+ dependencies {
+ implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0")
+ implementation(kotlin("stdlib-common"))
+ implementation(project(":flatbuffers-kotlin"))
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.1")
+
+ //moshi
+ implementation("com.squareup.moshi:moshi-kotlin:1.11.0")
+
+ //gson
+ implementation("com.google.code.gson:gson:2.8.5")
+ }
+ }
+
+ /* Targets configuration omitted.
+ * To find out how to configure the targets, please follow the link:
+ * https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets
+ */
+ targets {
+ targetFromPreset(presets.getAt("jvm"))
+ }
+ }
+}
+
+// This task download all JSON files used for benchmarking
+tasks.register<de.undercouch.gradle.tasks.download.Download>("downloadMultipleFiles") {
+ // We are downloading json benchmark samples from serdes-rs project.
+ // see: https://github.com/serde-rs/json-benchmark/blob/master/data
+ val baseUrl = "https://github.com/serde-rs/json-benchmark/raw/master/data/"
+ src(listOf("$baseUrl/canada.json", "$baseUrl/twitter.json", "$baseUrl/citm_catalog.json"))
+ dest(File("${project.projectDir.absolutePath}/src/jvmMain/resources"))
+ overwrite(false)
+}
+
+project.tasks.named("compileKotlinJvm") {
+ dependsOn("downloadMultipleFiles")
+}
diff --git a/kotlin/benchmark/src/jvmMain/java b/kotlin/benchmark/src/jvmMain/java
new file mode 120000
index 0000000..2260196
--- /dev/null
+++ b/kotlin/benchmark/src/jvmMain/java
@@ -0,0 +1 @@
+../../../../java/
\ No newline at end of file
diff --git a/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/FlexBuffersBenchmark.kt b/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/FlexBuffersBenchmark.kt
new file mode 100644
index 0000000..ade57d9
--- /dev/null
+++ b/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/FlexBuffersBenchmark.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.benchmark
+
+import com.google.flatbuffers.ArrayReadWriteBuf
+import com.google.flatbuffers.FlexBuffers
+import com.google.flatbuffers.FlexBuffersBuilder.BUILDER_FLAG_SHARE_ALL
+import com.google.flatbuffers.kotlin.FlexBuffersBuilder
+import com.google.flatbuffers.kotlin.getRoot
+import kotlinx.benchmark.Blackhole
+import org.openjdk.jmh.annotations.Benchmark
+import org.openjdk.jmh.annotations.BenchmarkMode
+import org.openjdk.jmh.annotations.Measurement
+import org.openjdk.jmh.annotations.Mode
+import org.openjdk.jmh.annotations.OutputTimeUnit
+import org.openjdk.jmh.annotations.Scope
+import org.openjdk.jmh.annotations.Setup
+import org.openjdk.jmh.annotations.State
+import java.util.concurrent.TimeUnit
+
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Measurement(iterations = 20, time = 1, timeUnit = TimeUnit.NANOSECONDS)
+class FlexBuffersBenchmark {
+
+ var initialCapacity = 1024
+ var value: Double = 0.0
+ val stringKey = Array(500) { "Ḧ̵̘́ȩ̵̐myFairlyBigKey$it" }
+ val stringValue = Array(500) { "Ḧ̵̘́ȩ̵̐myFairlyBigValue$it" }
+ val bigIntArray = IntArray(5000) { it }
+
+ @Setup
+ fun setUp() {
+ value = 3.0
+ }
+
+ @Benchmark
+ fun mapKotlin(blackhole: Blackhole) {
+ val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
+ kBuilder.putMap {
+ this["hello"] = "world"
+ this["int"] = 10
+ this["float"] = 12.3
+ this["intarray"] = bigIntArray
+ this.putMap("myMap") {
+ this["cool"] = "beans"
+ }
+ }
+ val ref = getRoot(kBuilder.finish())
+ val map = ref.toMap()
+ blackhole.consume(map.size)
+ blackhole.consume(map["hello"].toString())
+ blackhole.consume(map["int"].toInt())
+ blackhole.consume(map["float"].toDouble())
+ blackhole.consume(map["intarray"].toIntArray())
+ blackhole.consume(ref["myMap"]["cool"].toString())
+ blackhole.consume(ref["invalid_key"].isNull)
+ }
+
+ @Benchmark
+ fun mapJava(blackhole: Blackhole) {
+ val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
+ val startMap = jBuilder.startMap()
+ jBuilder.putString("hello", "world")
+ jBuilder.putInt("int", 10)
+ jBuilder.putFloat("float", 12.3)
+
+ val startVec = jBuilder.startVector()
+ bigIntArray.forEach { jBuilder.putInt(it) }
+ jBuilder.endVector("intarray", startVec, true, false)
+
+ val startInnerMap = jBuilder.startMap()
+ jBuilder.putString("cool", "beans")
+ jBuilder.endMap("myMap", startInnerMap)
+
+ jBuilder.endMap(null, startMap)
+ val ref = FlexBuffers.getRoot(jBuilder.finish())
+ val map = ref.asMap()
+ blackhole.consume(map.size())
+ blackhole.consume(map.get("hello").toString())
+ blackhole.consume(map.get("int").asInt())
+ blackhole.consume(map.get("float").asFloat())
+ val vec = map.get("intarray").asVector()
+ blackhole.consume(IntArray(vec.size()) { vec.get(it).asInt() })
+
+ blackhole.consume(ref.asMap()["myMap"].asMap()["cool"].toString())
+ blackhole.consume(ref.asMap()["invalid_key"].isNull)
+ }
+
+ @Benchmark
+ fun intArrayKotlin(blackhole: Blackhole) {
+ val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
+ kBuilder.put(bigIntArray)
+ val root = getRoot(kBuilder.finish())
+ blackhole.consume(root.toIntArray())
+ }
+
+ @Benchmark
+ fun intArrayJava(blackhole: Blackhole) {
+ val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
+ val v = jBuilder.startVector()
+ bigIntArray.forEach { jBuilder.putInt(it) }
+ jBuilder.endVector(null, v, true, false)
+ jBuilder.finish()
+ val root = FlexBuffers.getRoot(jBuilder.buffer)
+ val vec = root.asVector()
+ blackhole.consume(
+ IntArray(vec.size()) {
+ vec[it].asInt()
+ }
+ )
+ }
+
+ @Benchmark
+ fun stringArrayKotlin(blackhole: Blackhole) {
+ val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
+ kBuilder.putVector { stringValue.forEach { kBuilder.put(it) } }
+ kBuilder.finish()
+ val root = getRoot(kBuilder.buffer)
+ val vec = root.toVector()
+ blackhole.consume(Array(vec.size) { vec[it].toString() })
+ }
+
+ @Benchmark
+ fun stringArrayJava(blackhole: Blackhole) {
+ val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
+ val v = jBuilder.startVector()
+ stringValue.forEach { jBuilder.putString(it) }
+ jBuilder.endVector(null, v, false, false)
+ jBuilder.finish()
+ val root = FlexBuffers.getRoot(jBuilder.buffer)
+ val vec = root.asVector()
+ blackhole.consume(Array(vec.size()) { vec[it].toString() })
+ }
+
+ @Benchmark
+ fun stringMapKotlin(blackhole: Blackhole) {
+ val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
+ val pos = kBuilder.startMap()
+ for (i in stringKey.indices) {
+ kBuilder[stringKey[i]] = stringValue[i]
+ }
+ kBuilder.endMap(pos)
+ val ref = getRoot(kBuilder.finish())
+ val map = ref.toMap()
+ val keys = map.keys
+
+ for (key in keys) {
+ blackhole.consume(map[key.toString()].toString())
+ }
+ }
+
+ @Benchmark
+ fun stringMapBytIndexKotlin(blackhole: Blackhole) {
+ val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
+ val pos = kBuilder.startMap()
+ for (i in stringKey.indices) {
+ kBuilder[stringKey[i]] = stringValue[i]
+ }
+ kBuilder.endMap(pos)
+ val ref = getRoot(kBuilder.finish())
+ val map = ref.toMap()
+ for (index in 0 until map.size) {
+ blackhole.consume(map[index].toString())
+ }
+ }
+
+ @Benchmark
+ fun stringMapJava(blackhole: Blackhole) {
+ val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
+ val v = jBuilder.startMap()
+ for (i in stringKey.indices) {
+ jBuilder.putString(stringKey[i], stringValue[i])
+ }
+ jBuilder.endMap(null, v)
+ val ref = FlexBuffers.getRoot(jBuilder.finish())
+ val map = ref.asMap()
+ val keyVec = map.keys()
+ for (i in 0 until keyVec.size()) {
+ val s = keyVec[i].toString()
+ blackhole.consume(map[s].toString())
+ }
+ }
+}
diff --git a/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/JsonBenchmark.kt b/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/JsonBenchmark.kt
new file mode 100644
index 0000000..7d2ae50
--- /dev/null
+++ b/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/JsonBenchmark.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.benchmark
+
+import com.google.flatbuffers.kotlin.ArrayReadBuffer
+import com.google.flatbuffers.kotlin.JSONParser
+import com.google.flatbuffers.kotlin.Reference
+import com.google.flatbuffers.kotlin.toJson
+import com.google.gson.Gson
+import com.google.gson.JsonObject
+import com.google.gson.JsonParser
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import kotlinx.benchmark.Blackhole
+import okio.Buffer
+import org.openjdk.jmh.annotations.Benchmark
+import org.openjdk.jmh.annotations.BenchmarkMode
+import org.openjdk.jmh.annotations.Measurement
+import org.openjdk.jmh.annotations.Mode
+import org.openjdk.jmh.annotations.OutputTimeUnit
+import org.openjdk.jmh.annotations.Scope
+import org.openjdk.jmh.annotations.State
+import java.io.ByteArrayInputStream
+import java.io.InputStreamReader
+import java.util.concurrent.TimeUnit
+
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Measurement(iterations = 100, time = 1, timeUnit = TimeUnit.MICROSECONDS)
+class JsonBenchmark {
+
+ final val moshi = Moshi.Builder()
+ .addLast(KotlinJsonAdapterFactory())
+ .build()
+ final val moshiAdapter = moshi.adapter(Map::class.java)
+
+ final val gson = Gson()
+ final val gsonParser = JsonParser()
+
+ val fbParser = JSONParser()
+
+ final val twitterData = this.javaClass.classLoader.getResourceAsStream("twitter.json")!!.readBytes()
+ final val canadaData = this.javaClass.classLoader.getResourceAsStream("canada.json")!!.readBytes()
+ final val citmData = this.javaClass.classLoader.getResourceAsStream("citm_catalog.json")!!.readBytes()
+
+ val fbCitmRef = JSONParser().parse(ArrayReadBuffer(citmData))
+ val moshiCitmRef = moshi.adapter(Map::class.java).fromJson(citmData.decodeToString())
+ val gsonCitmRef = gsonParser.parse(citmData.decodeToString())
+
+ fun readFlexBuffers(data: ByteArray): Reference = fbParser.parse(ArrayReadBuffer(data))
+
+ fun readMoshi(data: ByteArray): Map<*, *>? {
+ val buffer = Buffer().write(data)
+ return moshiAdapter.fromJson(buffer)
+ }
+
+ fun readGson(data: ByteArray): JsonObject {
+ val parser = JsonParser()
+ val jsonReader = InputStreamReader(ByteArrayInputStream(data))
+ return parser.parse(jsonReader).asJsonObject
+ }
+
+ // TWITTER
+ @Benchmark
+ fun readTwitterFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(twitterData))
+ @Benchmark
+ fun readTwitterMoshi(hole: Blackhole?) = hole?.consume(readMoshi(twitterData))
+ @Benchmark
+ fun readTwitterGson(hole: Blackhole?) = hole?.consume(readGson(twitterData))
+
+ @Benchmark
+ fun roundTripTwitterFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(twitterData).toJson())
+ @Benchmark
+ fun roundTripTwitterMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(twitterData)))
+ @Benchmark
+ fun roundTripTwitterGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(twitterData)))
+
+ // CITM
+ @Benchmark
+ fun readCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(citmData))
+ @Benchmark
+ fun readCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(citmData)))
+ @Benchmark
+ fun readCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(citmData)))
+
+ @Benchmark
+ fun roundTripCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(citmData).toJson())
+ @Benchmark
+ fun roundTripCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(citmData)))
+ @Benchmark
+ fun roundTripCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(citmData)))
+
+ @Benchmark
+ fun writeCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(fbCitmRef.toJson())
+ @Benchmark
+ fun writeCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(moshiCitmRef))
+ @Benchmark
+ fun writeCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(gsonCitmRef))
+
+ // CANADA
+ @Benchmark
+ fun readCanadaFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(canadaData))
+ @Benchmark
+ fun readCanadaMoshi(hole: Blackhole?) = hole?.consume(readMoshi(canadaData))
+ @Benchmark
+ fun readCanadaGson(hole: Blackhole?) = hole?.consume(readGson(canadaData))
+}
diff --git a/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/UTF8Benchmark.kt b/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/UTF8Benchmark.kt
new file mode 100644
index 0000000..6fa2882
--- /dev/null
+++ b/kotlin/benchmark/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/benchmark/UTF8Benchmark.kt
@@ -0,0 +1,235 @@
+/*
+ * 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.benchmark
+
+import com.google.flatbuffers.kotlin.ArrayReadWriteBuffer
+import com.google.flatbuffers.kotlin.Key
+import com.google.flatbuffers.kotlin.Utf8
+import kotlinx.benchmark.Blackhole
+import org.openjdk.jmh.annotations.Benchmark
+import org.openjdk.jmh.annotations.BenchmarkMode
+import org.openjdk.jmh.annotations.Measurement
+import org.openjdk.jmh.annotations.Mode
+import org.openjdk.jmh.annotations.OutputTimeUnit
+import org.openjdk.jmh.annotations.Scope
+import org.openjdk.jmh.annotations.Setup
+import org.openjdk.jmh.annotations.State
+import java.nio.ByteBuffer
+import java.util.concurrent.TimeUnit
+import kotlin.random.Random
+
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Measurement(iterations = 100, time = 1, timeUnit = TimeUnit.MICROSECONDS)
+class UTF8Benchmark {
+
+ private final val sampleSize = 5000
+ private final val stringSize = 25
+ final var sampleSmallUtf8 = (0..sampleSize).map { populateUTF8(stringSize) }.toList()
+ final var sampleSmallUtf8Decoded = sampleSmallUtf8.map { it.encodeToByteArray() }.toList()
+ final var sampleSmallAscii = (0..sampleSize).map { populateAscii(stringSize) }.toList()
+ final var sampleSmallAsciiDecoded = sampleSmallAscii.map { it.encodeToByteArray() }.toList()
+
+ @Setup
+ fun setUp() {
+ }
+
+ @Benchmark
+ fun encodeUtf8KotlinStandard(blackhole: Blackhole) {
+ for (i in sampleSmallUtf8) {
+ blackhole.consume(i.encodeToByteArray())
+ }
+ }
+ @Benchmark
+ fun encodeUtf8KotlinFlatbuffers(blackhole: Blackhole) {
+ for (i in sampleSmallUtf8) {
+ val byteArray = ByteArray((i.length * 4))
+ blackhole.consume(Utf8.encodeUtf8Array(i, byteArray, 0, byteArray.size))
+ }
+ }
+ @Benchmark
+ fun encodeUtf8JavaFlatbuffers(blackhole: Blackhole) {
+ val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
+ for (i in sampleSmallUtf8) {
+ val byteBuffer = ByteBuffer.wrap(ByteArray(i.length * 4))
+ blackhole.consume(javaUtf8.encodeUtf8(i, byteBuffer))
+ }
+ }
+
+ @Benchmark
+ fun decodeUtf8KotlinStandard(blackhole: Blackhole) {
+ for (ary in sampleSmallUtf8Decoded) {
+ blackhole.consume(ary.decodeToString())
+ }
+ }
+
+ @Benchmark
+ fun decodeUtf8KotlinFlatbuffers(blackhole: Blackhole) {
+ for (ary in sampleSmallUtf8Decoded) {
+ blackhole.consume(Utf8.decodeUtf8Array(ary, 0, ary.size))
+ }
+ }
+
+ @Benchmark
+ fun decodeUtf8JavaFlatbuffers(blackhole: Blackhole) {
+ val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
+ for (ary in sampleSmallUtf8Decoded) {
+ val byteBuffer = ByteBuffer.wrap(ary)
+ blackhole.consume(javaUtf8.decodeUtf8(byteBuffer, 0, ary.size))
+ }
+ }
+
+ // ASCII TESTS
+
+ @Benchmark
+ fun encodeAsciiKotlinStandard(blackhole: Blackhole) {
+ for (i in sampleSmallAscii) {
+ blackhole.consume(i.encodeToByteArray())
+ }
+ }
+ @Benchmark
+ fun encodeAsciiKotlinFlatbuffers(blackhole: Blackhole) {
+ for (i in sampleSmallAscii) {
+ val byteArray = ByteArray(i.length) // Utf8.encodedLength(i))
+ blackhole.consume(Utf8.encodeUtf8Array(i, byteArray, 0, byteArray.size))
+ }
+ }
+ @Benchmark
+ fun encodeAsciiJavaFlatbuffers(blackhole: Blackhole) {
+ val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
+ for (i in sampleSmallAscii) {
+ val byteBuffer = ByteBuffer.wrap(ByteArray(i.length))
+ blackhole.consume(javaUtf8.encodeUtf8(i, byteBuffer))
+ }
+ }
+
+ @Benchmark
+ fun decodeAsciiKotlinStandard(blackhole: Blackhole) {
+
+ for (ary in sampleSmallAsciiDecoded) {
+ String(ary)
+ blackhole.consume(ary.decodeToString())
+ }
+ }
+
+ @Benchmark
+ fun decodeAsciiKotlinFlatbuffers(blackhole: Blackhole) {
+ for (ary in sampleSmallAsciiDecoded) {
+ blackhole.consume(Utf8.decodeUtf8Array(ary, 0, ary.size))
+ }
+ }
+
+ @Benchmark
+ fun decodeAsciiJavaFlatbuffers(blackhole: Blackhole) {
+ val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
+ for (ary in sampleSmallAsciiDecoded) {
+ val byteBuffer = ByteBuffer.wrap(ary)
+ blackhole.consume(javaUtf8.decodeUtf8(byteBuffer, 0, ary.size))
+ }
+ }
+
+ @Benchmark
+ fun readAllCharsString(blackhole: Blackhole) {
+ for (ary in sampleSmallAsciiDecoded) {
+ val key = Utf8.decodeUtf8Array(ary, 0, ary.size)
+ for (i in key.indices) {
+ blackhole.consume(key[i])
+ }
+ }
+ }
+
+ @Benchmark
+ fun readAllCharsCharSequence(blackhole: Blackhole) {
+ for (ary in sampleSmallAsciiDecoded) {
+ val key = Key(ArrayReadWriteBuffer(ary), 0, ary.size)
+ for (i in 0 until key.sizeInChars) {
+ blackhole.consume(key[i])
+ }
+ }
+ }
+
+ fun populateAscii(size: Int): String {
+ val data = ByteArray(size)
+ for (i in data.indices) {
+ data[i] = Random.nextInt(0, 127).toByte()
+ }
+
+ return String(data, 0, data.size)
+ }
+
+ // generate a string having at least length N
+ // can exceed by up to 3 chars, returns the actual length
+ fun populateUTF8(size: Int): String {
+ val data = ByteArray(size + 3)
+ var i = 0
+ while (i < size) {
+ val w = Random.nextInt() and 0xFF
+ when {
+ w < 0x80 -> data[i++] = 0x20; // w;
+ w < 0xE0 -> {
+ data[i++] = (0xC2 + Random.nextInt() % (0xDF - 0xC2 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w == 0xE0 -> {
+ data[i++] = w.toByte()
+ data[i++] = (0xA0 + Random.nextInt() % (0xBF - 0xA0 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w <= 0xEC -> {
+ data[i++] = w.toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w == 0xED -> {
+ data[i++] = w.toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0x9F - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w <= 0xEF -> {
+ data[i++] = w.toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w < 0xF0 -> {
+ data[i++] = (0xF1 + Random.nextInt() % (0xF3 - 0xF1 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w == 0xF0 -> {
+ data[i++] = w.toByte()
+ data[i++] = (0x90 + Random.nextInt() % (0xBF - 0x90 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w <= 0xF3 -> {
+ data[i++] = (0xF1 + Random.nextInt() % (0xF3 - 0xF1 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ w == 0xF4 -> {
+ data[i++] = w.toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0x8F - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
+ }
+ }
+ }
+ return String(data, 0, i)
+ }
+}
diff --git a/kotlin/build.gradle.kts b/kotlin/build.gradle.kts
new file mode 100644
index 0000000..455ee0d
--- /dev/null
+++ b/kotlin/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ id("com.diffplug.spotless") version "5.8.2"
+}
+
+group = "com.google.flatbuffers"
+version = "2.0.0-SNAPSHOT"
+
+subprojects {
+
+ repositories {
+ maven { setUrl("https://plugins.gradle.org/m2/") }
+ mavenCentral()
+ }
+}
+
+buildscript {
+ repositories {
+ maven { setUrl("https://plugins.gradle.org/m2/") }
+ gradlePluginPortal()
+ mavenCentral()
+ }
+}
+
+// plugin used to enforce code style
+spotless {
+ val klintConfig = mapOf("indent_size" to "2", "continuation_indent_size" to "2")
+ kotlin {
+ target("**/*.kt")
+ ktlint("0.40.0").userData(klintConfig)
+ trimTrailingWhitespace()
+ indentWithSpaces()
+ endWithNewline()
+ licenseHeaderFile("$rootDir/spotless/spotless.kt").updateYearWithLatest(false)
+ targetExclude("**/spotless.kt", "**/build/**")
+ }
+ kotlinGradle {
+ target("*.gradle.kts")
+ ktlint().userData(klintConfig)
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/build.gradle.kts b/kotlin/flatbuffers-kotlin/build.gradle.kts
new file mode 100644
index 0000000..b74807e
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/build.gradle.kts
@@ -0,0 +1,97 @@
+plugins {
+ kotlin("multiplatform") version "1.4.20"
+}
+
+group = "com.google.flatbuffers.kotlin"
+version = "2.0.0-SNAPSHOT"
+
+kotlin {
+ explicitApi()
+ jvm()
+ js {
+ browser {
+ binaries.executable()
+ testTask {
+ useKarma {
+ useChromeHeadless()
+ }
+ }
+ }
+ }
+ macosX64()
+ iosArm32()
+ iosArm64()
+ iosX64()
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(kotlin("stdlib-common"))
+ }
+ }
+
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test-common"))
+ implementation(kotlin("test-annotations-common"))
+ }
+ }
+ val jvmTest by getting {
+ dependencies {
+ implementation(kotlin("test-junit"))
+ }
+ }
+ val jvmMain by getting {
+ kotlin.srcDir("java")
+ dependencies {
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.1")
+ }
+ }
+
+ val jsMain by getting {
+ dependsOn(commonMain)
+ }
+ val jsTest by getting {
+ dependsOn(commonTest)
+ dependencies {
+ implementation(kotlin("test-js"))
+ }
+ }
+ val nativeMain by creating {
+ dependsOn(commonMain)
+ }
+ val nativeTest by creating {
+ dependsOn(commonMain)
+ }
+ val macosX64Main by getting {
+ dependsOn(nativeMain)
+ }
+
+ val iosArm32Main by getting {
+ dependsOn(nativeMain)
+ }
+ val iosArm64Main by getting {
+ dependsOn(nativeMain)
+ }
+ val iosX64Main by getting {
+ dependsOn(nativeMain)
+ }
+
+ all {
+ languageSettings.enableLanguageFeature("InlineClasses")
+ languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
+ }
+ }
+
+ /* Targets configuration omitted.
+ * To find out how to configure the targets, please follow the link:
+ * https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets */
+ targets {
+ targetFromPreset(presets.getAt("jvm"))
+ targetFromPreset(presets.getAt("js"))
+ targetFromPreset(presets.getAt("macosX64"))
+ targetFromPreset(presets.getAt("iosArm32"))
+ targetFromPreset(presets.getAt("iosArm64"))
+ targetFromPreset(presets.getAt("iosX64"))
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Buffers.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Buffers.kt
new file mode 100644
index 0000000..9851d90
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Buffers.kt
@@ -0,0 +1,489 @@
+/*
+ * 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 an 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, size: Int): String
+
+ /**
+ * Expose [ReadBuffer] as an array of bytes.
+ * This method is meant to be as efficient as possible, so for a 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()
+
+ /**
+ * 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, length: Int)
+
+ /**
+ * 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)
+
+ /**
+ * Writea [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: String, 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 operator fun set(dstIndex: Int, src: ByteArray, srcStart: Int, 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)
+
+ /**
+ * Current position of the buffer to be written. It will be automatically updated on [put] operations.
+ */
+ public var writePosition: Int
+
+ /**
+ * Defines the size of the message in the buffer. It also determines last position that buffer
+ * can be read or write. Last byte to be accessed is in position `limit() -1`.
+ * @return indicate last position
+ */
+ override val limit: Int
+
+ /**
+ * Request capacity of the buffer. 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.
+ */
+ public fun requestCapacity(capacity: Int)
+}
+
+public open class ArrayReadBuffer(protected var buffer: ByteArray, override val limit: Int = buffer.size) : ReadBuffer {
+
+ override fun findFirst(value: Byte, start: Int, end: Int): Int {
+ val e = min(end, limit)
+ val s = max(0, start)
+ for (i in s until e) if (buffer[i] == value) return i
+ return -1
+ }
+
+ override fun getBoolean(index: Int): Boolean = buffer[index] != 0.toByte()
+
+ override operator fun get(index: Int): Byte = buffer[index]
+
+ override fun getUByte(index: Int): UByte = buffer.getUByte(index)
+
+ override fun getShort(index: Int): Short = buffer.getShort(index)
+
+ override fun getUShort(index: Int): UShort = buffer.getUShort(index)
+
+ override fun getInt(index: Int): Int = buffer.getInt(index)
+
+ override fun getUInt(index: Int): UInt = buffer.getUInt(index)
+
+ override fun getLong(index: Int): Long = buffer.getLong(index)
+
+ override fun getULong(index: Int): ULong = buffer.getULong(index)
+
+ override fun getFloat(index: Int): Float = buffer.getFloat(index)
+
+ override fun getDouble(index: Int): Double = buffer.getDouble(index)
+
+ override fun getString(start: Int, size: Int): String = buffer.decodeToString(start, start + size)
+
+ override fun data(): ByteArray = buffer
+
+ override fun slice(start: Int, size: Int): ReadBuffer = ArrayReadBuffer(buffer, limit)
+}
+/**
+ * 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 a undefined behavior
+ *
+ * All operations assumes Little Endian byte order.
+ */
+public class ArrayReadWriteBuffer(
+ buffer: ByteArray,
+ override var writePosition: Int = 0
+) : ArrayReadBuffer(buffer, 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: 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: String, encodedLength: Int): Int {
+ val length = if (encodedLength != -1) encodedLength else Utf8.encodedLength(value)
+ withCapacity(writePosition + length) {
+ writePosition = setString(writePosition, value)
+ }
+ return length
+ }
+
+ override fun set(index: Int, value: Boolean) {
+ set(index, if (value) 1.toByte() else 0.toByte())
+ }
+
+ override operator fun set(dstIndex: Int, src: ByteArray, srcStart: Int, srcLength: Int) {
+ withCapacity(dstIndex + (srcLength + srcStart)) {
+ src.copyInto(buffer, dstIndex, srcStart, srcStart + srcLength)
+ }
+ }
+
+ override operator fun set(index: Int, value: Byte): Unit = withCapacity(index + 1) { set(index, value) }
+ override operator fun set(index: Int, value: UByte): Unit = withCapacity(index + 1) { setUByte(index, value) }
+ override operator fun set(index: Int, value: Short): Unit = withCapacity(index + 2) { setShort(index, value) }
+ override operator fun set(index: Int, value: UShort): Unit = withCapacity(index + 2) { setUShort(index, value) }
+ override operator fun set(index: Int, value: Int): Unit = withCapacity(index + 4) { setInt(index, value) }
+ override operator fun set(index: Int, value: UInt): Unit = withCapacity(index + 4) { setUInt(index, value) }
+ override operator fun set(index: Int, value: Long): Unit = withCapacity(index + 8) { setLong(index, value) }
+ override operator fun set(index: Int, value: ULong): Unit = withCapacity(index + 8) { setULong(index, value) }
+ override operator fun set(index: Int, value: Float): Unit = withCapacity(index + 4) { setFloat(index, value) }
+ override operator fun set(index: Int, value: Double): Unit = withCapacity(index + 8) { setDouble(index, value) }
+
+ override fun requestCapacity(capacity: Int) {
+ if (capacity < 0) error("Capacity may not be negative (likely a previous int overflow)")
+
+ if (buffer.size >= capacity) return
+ // implemented in the same growing fashion as ArrayList
+ val oldCapacity = buffer.size
+ var newCapacity = oldCapacity + (oldCapacity shr 1)
+ if (newCapacity < capacity) { // Note: this also catches newCapacity int overflow
+ newCapacity = capacity
+ }
+ buffer = buffer.copyOf(newCapacity)
+ }
+
+ private inline fun withCapacity(size: Int, crossinline action: ByteArray.() -> Unit) {
+ requestCapacity(size)
+ buffer.action()
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
new file mode 100644
index 0000000..68fd0f3
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.setString(index: Int, value: String): 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)
+internal 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 idx = index
+ var i = value.toInt()
+ ary[idx++] = (i and 0xff).toByte()
+ ary[idx++] = (i shr 8 and 0xff).toByte()
+ ary[idx++] = (i shr 16 and 0xff).toByte()
+ ary[idx++] = (i shr 24 and 0xff).toByte()
+ i = (value shr 32).toInt()
+ ary[idx++] = (i and 0xff).toByte()
+ ary[idx++] = (i shr 8 and 0xff).toByte()
+ ary[idx++] = (i shr 16 and 0xff).toByte()
+ ary[idx] = (i shr 24 and 0xff).toByte()
+ }
+
+ 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) {
+ var idx = index
+ val iValue: Int = value.toRawBits()
+ ary[idx++] = (iValue and 0xff).toByte()
+ ary[idx++] = (iValue shr 8 and 0xff).toByte()
+ ary[idx++] = (iValue shr 16 and 0xff).toByte()
+ ary[idx] = (iValue shr 24 and 0xff).toByte()
+ }
+
+ public inline fun setDouble(ary: ByteArray, index: Int, value: Double) {
+ var idx = index
+ val lValue: Long = value.toRawBits()
+ var i = lValue.toInt()
+ ary[idx++] = (i and 0xff).toByte()
+ ary[idx++] = (i shr 8 and 0xff).toByte()
+ ary[idx++] = (i shr 16 and 0xff).toByte()
+ ary[idx++] = (i shr 24 and 0xff).toByte()
+ i = (lValue shr 32).toInt()
+ ary[idx++] = (i and 0xff).toByte()
+ ary[idx++] = (i shr 8 and 0xff).toByte()
+ ary[idx++] = (i shr 16 and 0xff).toByte()
+ ary[idx] = (i shr 24 and 0xff).toByte()
+ }
+
+ 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))
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffers.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffers.kt
new file mode 100644
index 0000000..a22dd13
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffers.kt
@@ -0,0 +1,905 @@
+/*
+ * 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,
+ internal 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.toInt() >= 0x80) {
+ break
+ }
+ val b: Byte = buffer[bufferPos]
+ when {
+ b == ZeroByte -> return -c2.toInt()
+ b < 0 -> break
+ b != c2.toByte() -> return b - c2.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
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffersBuilder.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffersBuilder.kt
new file mode 100644
index 0000000..a4cd9d3
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffersBuilder.kt
@@ -0,0 +1,771 @@
+/*
+ * 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 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 assert, 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))
+ 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
+ buffer.put(key)
+ buffer.put(ZeroByte)
+ pos
+ }
+ } else {
+ val pos: Int = buffer.writePosition
+ buffer.put(key)
+ 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 size = Utf8.encodedLength(s)
+ val bitWidth = size.toULong().widthInUBits()
+ val byteWidth = align(bitWidth)
+
+ writeInt(size, byteWidth)
+
+ val sloc: Int = buffer.writePosition
+ if (size > 0)
+ buffer.put(s, size)
+ buffer.put(ZeroByte)
+ return Value(T_STRING, key, bitWidth, sloc.toULong())
+ }
+
+ private fun writeDouble(toWrite: Double, byteWidth: ByteWidth): Unit = when (byteWidth.value) {
+ 4 -> buffer.put(toWrite.toFloat())
+ 8 -> buffer.put(toWrite)
+ else -> Unit
+ }
+
+ private fun writeOffset(toWrite: Int, byteWidth: ByteWidth) {
+ 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.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) {
+ val byteWidth = Float.SIZE_BYTES
+ // since we know we are writing an array, we can avoid multiple copy/growth of the buffer by requesting
+ // the right size on the spot
+ buffer.requestCapacity(buffer.writePosition + (value.size * byteWidth))
+ value.forEach { buffer.put(it) }
+ }
+
+ private fun writeFloatArray(value: DoubleArray) {
+ val byteWidth = Double.SIZE_BYTES
+ // since we know we are writing an array, we can avoid multiple copy/growth of the buffer by requesting
+ // the right size on the spot
+ buffer.requestCapacity(buffer.writePosition + (value.size * byteWidth))
+ value.forEach { buffer.put(it) }
+ }
+
+ private inline fun writeIntegerArray(
+ start: Int,
+ size: Int,
+ byteWidth: ByteWidth,
+ crossinline valueBlock: (Int) -> ULong
+ ) {
+ // since we know we are writing an array, we can avoid multiple copy/growth of the buffer by requesting
+ // the right size on the spot
+ buffer.requestCapacity(buffer.writePosition + (size * byteWidth))
+ 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) = 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) = 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)
+ 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.
+ 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.
+ 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 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
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffersInternals.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffersInternals.kt
new file mode 100644
index 0000000..15d0027
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/FlexBuffersInternals.kt
@@ -0,0 +1,251 @@
+/*
+ * 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 inline class BitWidth(public val value: Int) {
+ public inline fun max(other: BitWidth): BitWidth = if (this.value >= other.value) this else other
+}
+
+public inline class ByteWidth(public val value: Int)
+
+public inline 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 a 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 val emptyBuffer = ArrayReadWriteBuffer(1)
+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
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/JSON.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/JSON.kt
new file mode 100644
index 0000000..ee20138
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/JSON.kt
@@ -0,0 +1,828 @@
+/*
+ * 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.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('{'.toByte())
+ // key values pairs
+ for (i in 0 until size) {
+ val key = keyAt(i)
+ out.jsonEscape(buffer, key.start, key.sizeInBytes)
+ out.put(':'.toByte())
+ get(i).toJson(out)
+ if (i != size - 1) {
+ out.put(','.toByte())
+ }
+ }
+ // close bracket
+ out.put('}'.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('['.toByte())
+ for (i in 0 until size) {
+ get(i).toJson(out)
+ if (i != size - 1) {
+ out.put(','.toByte())
+ }
+ }
+ out.put(']'.toByte())
+}
+
+/**
+ * JSONParser class is used to parse a JSON as FlexBuffers. Calling [JSONParser.parse] fiils [output]
+ * and returns a [Reference] ready to be used.
+ */
+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'.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.toInt() 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.toChar()
+ else -> makeError(data, "Invalid escape sequence.", byte1)
+ }
+ }
+
+ private fun Byte.print(): String = when (this) {
+ in 0x21..0x7E -> "'${this.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'.toInt()] = "\\n".encodeToByteArray()
+ this['\t'.toInt()] = "\\t".encodeToByteArray()
+ this['\r'.toInt()] = "\\r".encodeToByteArray()
+ this['\b'.toInt()] = "\\b".encodeToByteArray()
+ this[0x0c] = "\\f".encodeToByteArray()
+ this['"'.toInt()] = "\\\"".encodeToByteArray()
+ this['\\'.toInt()] = "\\\\".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.
+private inline 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
+ }
+}
+
+private inline 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'.toByte()
+private const val CHAR_OPEN_OBJECT = '{'.toByte()
+private const val CHAR_COLON = ':'.toByte()
+private const val CHAR_CLOSE_OBJECT = '}'.toByte()
+private const val CHAR_OPEN_ARRAY = '['.toByte()
+private const val CHAR_CLOSE_ARRAY = ']'.toByte()
+private const val CHAR_DOUBLE_QUOTE = '"'.toByte()
+private const val CHAR_BACKSLASH = '\\'.toByte()
+private const val CHAR_FORWARDSLASH = '/'.toByte()
+private const val CHAR_f = 'f'.toByte()
+private const val CHAR_a = 'a'.toByte()
+private const val CHAR_r = 'r'.toByte()
+private const val CHAR_t = 't'.toByte()
+private const val CHAR_n = 'n'.toByte()
+private const val CHAR_b = 'b'.toByte()
+private const val CHAR_e = 'e'.toByte()
+private const val CHAR_E = 'E'.toByte()
+private const val CHAR_u = 'u'.toByte()
+private const val CHAR_A = 'A'.toByte()
+private const val CHAR_F = 'F'.toByte()
+private const val CHAR_EOF = (-1).toByte()
+private const val CHAR_COMMA = ','.toByte()
+private const val CHAR_0 = '0'.toByte()
+private const val CHAR_1 = '1'.toByte()
+private const val CHAR_2 = '2'.toByte()
+private const val CHAR_3 = '3'.toByte()
+private const val CHAR_4 = '4'.toByte()
+private const val CHAR_5 = '5'.toByte()
+private const val CHAR_6 = '6'.toByte()
+private const val CHAR_7 = '7'.toByte()
+private const val CHAR_8 = '8'.toByte()
+private const val CHAR_9 = '9'.toByte()
+private const val CHAR_MINUS = '-'.toByte()
+private const val CHAR_PLUS = '+'.toByte()
+private const val CHAR_DOT = '.'.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
+)
diff --git a/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Utf8.kt b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Utf8.kt
new file mode 100644
index 0000000..4b02cc5
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonMain/kotlin/com/google/flatbuffers/kotlin/Utf8.kt
@@ -0,0 +1,416 @@
+/*
+ * 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].toInt() < 0x80) {
+ i++
+ }
+
+ // This loop optimizes for chars less than 0x800.
+ while (i < utf16Length) {
+ val c = sequence[i]
+ if (c.toInt() < 0x800) {
+ utf8Length += 0x7f - c.toInt() 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.toInt() < 0x800) {
+ utf8Length += 0x7f - c.toInt() 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.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.toInt() < 0x80) {
+ // One byte (0xxx xxxx)
+ out[0] = c.toByte()
+ 1
+ } else if (c.toInt() < 0x800) {
+ // Two bytes (110x xxxx 10xx xxxx)
+ out[0] = (0xC0 or (c.toInt() ushr 6)).toByte()
+ out[1] = (0x80 or (0x3F and c.toInt())).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.toInt() ushr 12)).toByte()
+ out[1] = (0x80 or (0x3F and (c.toInt() ushr 6))).toByte()
+ out[2] = (0x80 or (0x3F and c.toInt())).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 }.toInt() < 0x80) {
+ out[j + i] = cc.toByte()
+ i++
+ }
+ if (i == utf16Length) {
+ return j + utf16Length
+ }
+ j += i
+ var c: Char
+ while (i < utf16Length) {
+ c = input[i]
+ if (c.toInt() < 0x80 && j < limit) {
+ out[j++] = c.toByte()
+ } else if (c.toInt() < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
+ out[j++] = (0xF shl 6 or (c.toInt() ushr 6)).toByte()
+ out[j++] = (0x80 or (0x3F and c.toInt())).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.toInt() ushr 12)).toByte()
+ out[j++] = (0x80 or (0x3F and (c.toInt() ushr 6))).toByte()
+ out[j++] = (0x80 or (0x3F and c.toInt())).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.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.toInt()
+ }
+
+ private fun isSurrogatePair(high: Char, low: Char) = high.isHighSurrogate() and low.isLowSurrogate()
+
+ private fun toCodePoint(high: Char, low: Char): Int = (high.toInt() shl 10) + low.toInt() +
+ (MIN_SUPPLEMENTARY_CODE_POINT - (Char.MIN_HIGH_SURROGATE.toInt() shl 10) - Char.MIN_LOW_SURROGATE.toInt())
+
+ 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
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/ByteArrayTest.kt b/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/ByteArrayTest.kt
new file mode 100644
index 0000000..560b0f3
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/ByteArrayTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.setString(0, testSet)
+ assertArrayEquals(encoded, data)
+ assertEquals(testSet, data.getString(0, encoded.size))
+ }
+}
+
+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()}"
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/FlexBuffersTest.kt b/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/FlexBuffersTest.kt
new file mode 100644
index 0000000..71820b6
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/FlexBuffersTest.kt
@@ -0,0 +1,301 @@
+/*
+ * 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])
+ }
+ }
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/JSONTest.kt b/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/JSONTest.kt
new file mode 100644
index 0000000..0375bb5
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/commonTest/kotlin/com/google/flatbuffers/kotlin/JSONTest.kt
@@ -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()))
+ }
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/jsMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt b/kotlin/flatbuffers-kotlin/src/jsMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
new file mode 100644
index 0000000..99c6b51
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/jsMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
@@ -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)
diff --git a/kotlin/flatbuffers-kotlin/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt b/kotlin/flatbuffers-kotlin/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
new file mode 100644
index 0000000..7e5d376
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/jvmMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
@@ -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: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)
diff --git a/kotlin/flatbuffers-kotlin/src/jvmTest/kotlin/com/google/flatbuffers/kotlin/Utf8Test.kt b/kotlin/flatbuffers-kotlin/src/jvmTest/kotlin/com/google/flatbuffers/kotlin/Utf8Test.kt
new file mode 100644
index 0000000..96b9c0a
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/jvmTest/kotlin/com/google/flatbuffers/kotlin/Utf8Test.kt
@@ -0,0 +1,35 @@
+/*
+ * 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 utf8Lines = String(this.javaClass.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]))
+ }
+ }
+}
diff --git a/kotlin/flatbuffers-kotlin/src/jvmTest/resources/utf8_sample.txt b/kotlin/flatbuffers-kotlin/src/jvmTest/resources/utf8_sample.txt
new file mode 100644
index 0000000..4fea69b
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/jvmTest/resources/utf8_sample.txt
@@ -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: █
+ ▉
+ ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
+ ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
+ ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
+ ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
+ ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
+ ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
+ ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
diff --git a/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt b/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
new file mode 100644
index 0000000..e9dc087
--- /dev/null
+++ b/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt
@@ -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 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))
diff --git a/kotlin/gradle/wrapper/gradle-wrapper.jar b/kotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
--- /dev/null
+++ b/kotlin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/kotlin/gradle/wrapper/gradle-wrapper.properties b/kotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..8faf39d
--- /dev/null
+++ b/kotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,9 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+# Remove kotlin MPP warning
+kotlin.mpp.stability.nowarn=true
+# Needed to share source among different targets
+kotlin.mpp.enableGranularSourceSetsMetadata=true
diff --git a/kotlin/gradlew b/kotlin/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/kotlin/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/kotlin/gradlew.bat b/kotlin/gradlew.bat
new file mode 100644
index 0000000..5093609
--- /dev/null
+++ b/kotlin/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/kotlin/settings.gradle b/kotlin/settings.gradle
new file mode 100644
index 0000000..a30971c
--- /dev/null
+++ b/kotlin/settings.gradle
@@ -0,0 +1,10 @@
+pluginManagement {
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+
+rootProject.name = 'flatbuffers-kotlin'
+include 'flatbuffers-kotlin', "benchmark"
diff --git a/kotlin/spotless/spotless.kt b/kotlin/spotless/spotless.kt
new file mode 100644
index 0000000..6363ca0
--- /dev/null
+++ b/kotlin/spotless/spotless.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright $YEAR 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.
+ */