commit d077d20b8d1d5aaa2fceb9bd32c482ad5f2f6f9b Author: SisMaker <1713699517@qq.com> Date: Sat Nov 28 20:08:36 2020 +0800 初始化提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..613493f --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +.eunit +*.o +*.beam +*.plt +erl_crash.dump +.concrete/DEV_MODE + +# rebar 2.x +.rebar +rel/example_project +ebin/* +deps + +# rebar 3 +.rebar3 +_build/ +_checkouts/ +rebar.lock + +# idea +.idea +*.iml +rebar3.crashdump +*~ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fed8e7 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +erlVPack +===== +erlAarango 二进制序列化库 + +Build +----- + $ rebar3 compile diff --git a/VelocyPack.md b/VelocyPack.md new file mode 100644 index 0000000..b5bc081 --- /dev/null +++ b/VelocyPack.md @@ -0,0 +1,621 @@ +VelocyPack (VPack) +================== + + Version 1 + +VelocyPack (VPack) is a fast and compact serialization format + + +## Generalities + +VPack is (unsigned) byte oriented, so VPack values are simply sequences +of bytes and are platform independent. Values are not necessarily +aligned, so all access to larger subvalues must be properly organised to +avoid alignment assumptions of the CPU. + + +## Value types + +We describe a single VPack value, which is recursive in nature, but +resides (with two exceptions, see below) in a single contiguous block of +memory. Assume that the value starts at address A, the first byte V +indicates the type (and often the length) of the VPack value at hand: + +We first give an overview with a brief but accurate description for +reference, for arrays and objects see below for details: + + - 0x00 : none - this indicates absence of any type and value, + this is not allowed in VPack values + - 0x01 : empty array + - 0x02 : array without index table (all subitems have the same + byte length), 1-byte byte length + - 0x03 : array without index table (all subitems have the same + byte length), 2-byte byte length + - 0x04 : array without index table (all subitems have the same + byte length), 4-byte byte length + - 0x05 : array without index table (all subitems have the same + byte length), 8-byte byte length + - 0x06 : array with 1-byte index table offsets, bytelen and # subvals + - 0x07 : array with 2-byte index table offsets, bytelen and # subvals + - 0x08 : array with 4-byte index table offsets, bytelen and # subvals + - 0x09 : array with 8-byte index table offsets, bytelen and # subvals + - 0x0a : empty object + - 0x0b : object with 1-byte index table offsets, sorted by + attribute name, 1-byte bytelen and # subvals + - 0x0c : object with 2-byte index table offsets, sorted by + attribute name, 2-byte bytelen and # subvals + - 0x0d : object with 4-byte index table offsets, sorted by + attribute name, 4-byte bytelen and # subvals + - 0x0e : object with 8-byte index table offsets, sorted by + attribute name, 8-byte bytelen and # subvals + - 0x0f : object with 1-byte index table offsets, not sorted by + attribute name, 1-byte bytelen and # subvals + - 0x10 : object with 2-byte index table offsets, not sorted by + attribute name, 2-byte bytelen and # subvals + - 0x11 : object with 4-byte index table offsets, not sorted by + attribute name, 4-byte bytelen and # subvals + - 0x12 : object with 8-byte index table offsets, not sorted by + attribute name, 8-byte bytelen and # subvals + - 0x13 : compact array, no index table + - 0x14 : compact object, no index table + - 0x15-0x16 : reserved + - 0x17 : illegal - this type can be used to indicate a value that + is illegal in the embedding application + - 0x18 : null + - 0x19 : false + - 0x1a : true + - 0x1b : double IEEE-754, 8 bytes follow, stored as little + endian uint64 equivalent + - 0x1c : UTC-date in milliseconds since the epoch, stored as 8 byte + signed int, little endian, two's complement + - 0x1d : external (only in memory): a char* pointing to the actual + place in memory, where another VPack item resides, not + allowed in VPack values on disk or on the network + - 0x1e : minKey, nonsensical value that compares < than all other values + - 0x1f : maxKey, nonsensical value that compares > than all other values + - 0x20-0x27 : signed int, little endian, 1 to 8 bytes, number is V - 0x1f, + two's complement + - 0x28-0x2f : uint, little endian, 1 to 8 bytes, number is V - 0x27 + - 0x30-0x39 : small integers 0, 1, ... 9 + - 0x3a-0x3f : small negative integers -6, -5, ..., -1 + - 0x40-0xbe : UTF-8-string, using V - 0x40 bytes (not Unicode characters!), + length 0 is possible, so 0x40 is the empty string, + maximal length is 126, note that strings here are not + zero-terminated and may contain NUL bytes + - 0xbf : long UTF-8-string, next 8 bytes are length of string in + bytes (not Unicode characters) as little endian unsigned + integer, note that long strings are not zero-terminated + and may contain NUL bytes + - 0xc0-0xc7 : binary blob, next V - 0xbf bytes are the length of blob in + bytes, note that binary blobs are not zero-terminated + - 0xc8-0xcf : positive long packed BCD-encoded float, V - 0xc7 bytes follow + that encode in a little endian way the length of the + mantissa in bytes. Directly after that follow 4 bytes + encoding the (power of 10) exponent, by which the mantissa + is to be multiplied, stored as little endian two's + complement signed 32-bit integer. After that, as many + bytes follow as the length information at the beginning + has specified, each byte encodes two digits in + big-endian packed BCD + Example: 12345 decimal can be encoded as + 0xc8 0x03 0x00 0x00 0x00 0x00 0x01 0x23 0x45 + or 0xc8 0x03 0xff 0xff 0xff 0xff 0x12 0x34 0x50 + - 0xd0-0xd7 : negative long packed BCD-encoded float, V - 0xcf bytes + follow that encode in a little endian way the length of + the mantissa in bytes. After that, same as positive long + packed BCD-encoded float above. + - 0xd8-0xed : reserved + - 0xee-0xef : value tagging for logical types + - 0xf0-0xff : custom types + + +## Arrays + +Empty arrays are simply a single byte 0x01. + +We next describe the type cases 0x02 to 0x09, see below for the +special compact type 0x13. + +Non-empty arrays look like one of the following: + + one of 0x02 to 0x05 + BYTELENGTH + OPTIONAL UNUSED: padding + sub VPack values + +or + + 0x06 + BYTELENGTH in 1 byte + NRITEMS in 1 byte + OPTIONAL UNUSED: 6 bytes of padding + sub VPack values + INDEXTABLE with 1 byte per entry + +or + + 0x07 + BYTELENGTH in 2 bytes + NRITEMS in 2 bytes + OPTIONAL UNUSED: 4 bytes of padding + sub VPack values + INDEXTABLE with 4 byte per entry + +or + + 0x08 + BYTELENGTH in 4 bytes + NRITEMS in 4 bytes + sub VPack values + INDEXTABLE with 4 byte per entry + +or + + 0x09 + BYTELENGTH in 8 bytes + sub VPack values + INDEXTABLE with 8 byte per entry + NRITEMS in 8 bytes + +If any optional padding is allowed for a type, the padding must consist +of exactly that many bytes that the length of the padding, the length of +BYTELENGTH and the length of NRITEMS (if present) sums up to 8. If the +length of BYTELENGTH is already 8, there is no padding allowed. The +entire padding must consist of zero bytes (ASCII NUL). + +Numbers (for byte length, number of subvalues and offsets in the +INDEXTABLE) are little endian unsigned integers, using 1 byte for +types 0x02 and 0x06, 2 bytes for types 0x03 and 0x07, 4 bytes for types +0x04 and 0x08, and 8 bytes for types 0x05 and 0x09. + +NRITEMS is a single number as described above. + +The INDEXTABLE consists of: + - for types 0x06-0x09 an array of offsets (unaligned, in the number + format described above) earlier offsets reside at lower addresses. + Offsets are measured from the start of the VPack value. + + +Non-empty arrays of types 0x06 to 0x09 have a small header including +their byte length, the number of subvalues, then all the subvalues and +finally an index table containing offsets to the subvalues. To find the +index table, find the number of subvalues, then the end, and from that +the base of the index table, considering how wide its entries are. + +For types 0x02 to 0x05 there is no offset table and no number of items. +The first item begins at address A+2, A+3, A+5 or respectively A+9, +depending on the type and thus the width of the byte length field. Note +the following special rule: The actual position of the first subvalue +is allowed to be further back, after some run of padding zero bytes. + +For example, if 2 bytes are used for both the byte length (BYTELENGTH), +then an optional padding of 4 zero bytes is then allowed to follow, and +the actual VPack subvalues can start at A+9. +This is to give a program that builds a VPack value the opportunity to +reserve 8 bytes in the beginning and only later find out that fewer bytes +suffice to write the byte length. One can determine the number of +subvalues by finding the first subvalue, its byte length, and +dividing the amount of available space by it. + +For types 0x06 to 0x09 the offset table describes where the subvalues +reside. It is not necessary for the subvalues to start immediately after +the number of subvalues field. + +As above, it is allowed to include optional padding. Again here, any +padding must consist of a run of consecutive zero bytes (ASCII NUL) and +must be as long that it fills up the length of BYTELENGTH and the length +of NRITEMS to 8. + +For example, if both BYTELENGTH and NRITEMS can be expressed using 2 bytes +each, the sum of their lengths is 4. It is therefore allowed to add 4 +bytes of padding here, so that the first subvalue could be at address A+9. + +There is one exception for the 8-byte numbers case (type 0x05): +In this case the number of elements is moved behind the index table. +This is to get away without moving memory when one has reserved 8 bytes +in the beginning and later noticed that all 8 bytes are needed for the +byte length. For this case it is not allowed to include any padding. + +All offsets are measured from base A. + + +*Example*: + +`[1,2,3]` has the hex dump + + 02 05 31 32 33 + +in the most compact representation, but the following are equally +possible, though not necessarily advised to use: + +*Examples*: + + 03 06 00 31 32 33 + + 04 08 00 00 00 31 32 33 + + 05 0c 00 00 00 00 00 00 00 31 32 33 + + 06 09 03 31 32 33 03 04 05 + + 07 0e 00 03 00 31 32 33 05 00 06 00 07 00 + + 08 18 00 00 00 03 00 00 00 31 32 33 09 00 00 00 0a 00 00 00 0b 00 00 00 + + 09 + 2c 00 00 00 00 00 00 00 + 31 32 33 + 09 00 00 00 00 00 00 00 + 0a 00 00 00 00 00 00 00 + 0b 00 00 00 00 00 00 00 + 03 00 00 00 00 00 00 00 + +Note that it is not recommended to encode short arrays in too long a +format. + +We now describe the special type 0x13, which is useful for a +particularly compact array representation. Note that to some extent this +goes against the principles of the VelocyPack format, since quick access +to subvalues is no longer possible, all items in the array must be +scanned to find a particular one. However, there are certain use cases +for VelocyPack which only require sequential access (for example JSON +dumping) and have a particular need for compactness. + +The overall format of this array type is + + 0x13 as type byte + BYTELENGTH + sub VPack values + NRITEMS + +There is no index table at all, although the sub VelocyPack values can +have different byte sizes. The BYTELENGTH and NRITEMS are encoded in a +special format, which we describe now. + +The BYTELENGTH consists of 1 to 8 bytes, of which all but the last one +have their high bit set. Thus, the high bits determine, how many bytes +are actually used. The lower 7 bits of all these bits together comprise +the actual byte length in a little endian fashion. That is, the byte at +address A+1 contains the least significant 7 bits (0 to 6) of the byte length, +the following byte at address A+2 contains the bits 7 to 13, and so on. +Since the total number of bytes is limited to 8, this encodes unsigned +integers of up to 56 bits, which is the overall limit for the size of +such a compact array representation. + +The NRITEMS entry is encoded essentially the same, except that it is +laid out in reverse order in memory. That is, one has to use the +BYTELENGTH to find the end of the array value and go back bytes until +one finds a byte with high bit reset. The last byte (at the highest +memory address) contains the least significant 7 bits of the NRITEMS +value, the second one bits 7 to 13 and so on. + +Here is an example, the array [1, 16] can be encoded as follows: + + 13 06 + 31 28 10 + 02 + +## Objects + +Empty objects are simply a single byte 0x0a. + +We next describe the type cases 0x0b to 0x12, see below for the +special compact type 0x14. + +Non-empty objects look like this: + + one of 0x0b - 0x12 + BYTELENGTH + optional NRITEMS + sub VPack values as pairs of attribute and value + optional INDEXTABLE + NRITEMS for the 8-byte case + +Numbers (for byte length, number of subvalues and offsets in the +INDEXTABLE) are little endian unsigned integers, using 1 byte for +types 0x0b and 0x0f, 2 bytes for types 0x0c and 0x10, 4 bytes for types +0x0d and 0x11, and 8 bytes for types 0x0e and 0x12. + +NRITEMS is a single number as described above. + +The INDEXTABLE consists of: + - an array of offsets (unaligned, in the number format described + above) earlier offsets reside at lower addresses. + Offsets are measured from the beginning of the VPack value. + +Non-empty objects have a small header including their byte length, the +number of subvalues, then all the subvalues and finally an index table +containing offsets to the subvalues. To find the index table, find +number of subvalues, then the end, and from that the base of the index +table, considering how wide its entries are. + +For all types the offset table describes where the subvalues reside. It +is not necessary for the subvalues to start immediately after the number +of subvalues field. For performance reasons when building the value, it +could be desirable to reserve 8 bytes for the byte length and the number +of subvalues and not fill the gap, even though it turns out later that +offsets and thus the byte length only uses 2 bytes, say. + +There is one special case: the empty object is simply stored as the +single byte 0x0a. + +There is another exception: For 8-byte numbers (0x12) the number of +subvalues is stored behind the INDEXTABLE. This is to get away without +moving memory when one has reserved 8 bytes in the beginning and later +noticed that all 8 bytes are needed for the byte length. + +All offsets are measured from base A. + +Each entry consists of two parts, the key and the value, they are +encoded as normal VPack values as above, the first is always a short or +long UTF-8 string starting with a byte 0x40-0xbf as described below. The +second is any other VPack value. + +There is one extension: For the key it is possible to use the positive +small integer values 0x30-0x39 or an unsigned integer starting with a +type byte of 0x28-0x2f. Any such integer value is an index into an +outside-given table of attribute names. These are convenient when only +very few attribute names occur or some are repeated very often. The +standard way to encode such an attribute name table is as a VPack array +of strings as specified here. + +Objects are always stored with sorted key/value pairs, sorted by bytewise +comparions of the keys on each nesting level. Sorting has some overhead +but will allow looking up keys in logarithmic time later. Note that only the +index table needs to be sorted, it is not required that the offsets in +these tables are increasing. Since the index table resides after the actual +subvalues, one can build up a complex VPack value by writing linearly. + +Example: the object `{"a": 12, "b": true, "c": "xyz"}` can have the hexdump: + + 0b + 13 03 + 41 62 1a + 41 61 28 0c + 41 63 43 78 79 7a + 06 03 0a + +The same object could have been done with an index table with longer +entries, as in this example: + + 0d + 22 00 00 00 + 03 00 00 00 + 41 62 1a + 41 61 28 0c + 41 63 43 78 79 7a + 0c 00 00 00 09 00 00 00 10 00 00 00 + +Similarly with type 0x0c and 2-byte offsets, byte length and number of +subvalues, or with type 0x0e and 8-byte numbers. + +Note that it is not recommended to encode short objects with too long +index tables. + +### Special compact objects + +We now describe the special type 0x14, which is useful for a +particularly compact object representation. Note that to some extent +this goes against the principles of the VelocyPack format, since quick +access to subvalues is no longer possible, all key/value pairs in the +object must be scanned to find a particular one. However, there are +certain use cases for VelocyPack which only require sequential access +(for example JSON dumping) and have a particular need for compactness. + +The overall format of this object type is + + 0x14 as type byte + BYTELENGTH + sub VPack key/value pairs + NRPAIRS + +There is no index table at all, although the sub VelocyPack values can +have different byte sizes. The BYTELENGTH and NRPAIRS are encoded in a +special format, which we describe now. It is the same as for the special +compact array type 0x13, which we repeat here for the sake of +completeness. + +The BYTELENGTH consists of 1 to 8 bytes, of which all but the last one +have their high bit set. Thus, the high bits determine, how many bytes +are actually used. The lower 7 bits of all these bits together comprise +the actual byte length in a little endian fashion. That is, the byte at +address A+1 contains the least significant 7 bits (0 to 6) of the byte +length, the following byte at address A+2 contains the bits 7 to 13, and +so on. Since the total number of bytes is limited to 8, this encodes +unsigned integers of up to 56 bits, which is the overall limit for the +size of such a compact array representation. + +The NRPAIRS entry is encoded essentially the same, except that it +is laid out in reverse order in memory. That is, one has to use the +BYTELENGTH to find the end of the array value and go back bytes until +one finds a byte with high bit reset. The last byte (at the highest +memory address) contains the least significant 7 bits of the NRPAIRS +value, the second one bits 7 to 13 and so on. + +Here is an example, the object {"a":1, "b":16} can be encoded as follows: + + 14 0a + 41 61 31 42 62 28 10 + 02 + + +## Doubles + +Type 0x1b indicates a double IEEE-754 value using the 8 bytes following +the type byte. To guarantee platform-independentness the details of the +byte order are as follows. Encoding is done by using memcpy to copy the +internal double value to an uint64\_t. This 64-bit unsigned integer is +then stored as little endian 8 byte integer in the VPack value. Decoding +works in the opposite direction. This should sort out the undetermined +byte order in IEEE-754 in practice. + + +## Dates + +Type 0x1c indicates a signed 64-int integer stored in 8 bytes little +endian two's complement notation directly after the type. The value means +a universal UTC-time measured in milliseconds since the epoch, which is +00:00 on 1 January 1970 UTC. + + +## External VPack values + +This type is only for use within memory, not for data exchange over disk +or network. Therefore, we only need to specify that the following k +bytes are the memcpy of a char* on the current architecture. That char* +points to the actual VPack value elsewhere in memory. + + +## Artifical minimal and maximal keys + +These values of types 0x1e and 0x1f have no meaning other than comparing +smaller or greater respectively than any other VPack value. The idea is +that these can be used in systems that define a total order on all VPack +values to specify left or right ends of infinite intervals. + + +## Integer types + +There are different ways to specify integers. For small values -6 to 9 +inclusively there are specific type bytes in the range 0x30 to 0x3f to +allow for storage in a single byte. After that there are signed and +unsigned integer types that can code in the type byte the number of +bytes used (ranges 0x20-0x27 for signed and 0x28-0x2f for unsigned). + + +## Null and boolean values + +These three values use a single byte to store the corresponding JSON +values. + + +## Strings + +Strings are stored as UTF-8 encoded byte sequences. There are two +variants, a short one and a long one. In the short one, the byte length +(not the number of UTF-8 characters) is directly encoded in the type, +and this works up to and including byte length 126. Types 0x40 to 0xbe +are used for this and the byte length is V - 0x3f, if V is the type +byte. For strings longer than 126 bytes, the type byte is 0xbf and the +byte length of the string is stored in the first 8 bytes after the type +byte, using a little endian unsigned integer representation. The actual +string follows after these 8 bytes. There is no terminating zero byte in +either case and the string may contain zero bytes. + + +## Binary data + +The type bytes 0xc0 to 0xc7 allow to store arbitrary binary byte +sequences as a VPack value. The format is as follows: If V is the type +byte, then V - 0xbf bytes follow it to make a little endian unsigned +integer representing the length of the binary data, which directly +follows these length bytes. No alignment is guaranteed. The content is +entirely up to the user. + + +## Packed BCD long floats + +These types are used to represent arbitrary precision decimal numbers. +There are different types for positive and negative numbers. The overall +format of these values is: + + one of 0xc8 - 0xcf (positive) or of 0xd0 - 0xd7 (negative) + LENGTH OF MANTISSA in bytes + EXPONENT (as 4-byte little endian signed two's complement integer) + MANTISSA (as packed BCD-encoded integer, big-endian) + +The type byte describes the sign of the number as well as the number of +bytes used to specify the byte length of the mantissa. As usual, if V is +the type byte, then V - 0xc7 (in the positive case) or V - 0xcf (in the +negative case) bytes are used for the length of the mantissa, stored as +little endian unsigned integer directly after the byte length. After +this follow exactly 4 bytes (little endian signed two's complement +integer) to specify the exponent. After the exponent, the actual +mantissa bytes follow. + +Packed BCD is used, so that each byte stores exactly 2 decimal digits as +in 0x34 for the decimal digits 34. Therefore, the mantissa always has an +even number of decimal digits. Note that the mantissa is stored in big +endian form, to make parsing and dumping efficient. This leads to the +"unholy nibble problem": When a JSON parser sees the beginning of a +longish number, it does not know whether an even or odd number of digits +follow. However, for efficiency reasons it wants to start writing bytes +to the output as it reads the input. This is, where the exponent comes +to the rescue, which is illustrated by the following example: + + 12345 decimal can be encoded as: + + 0xc8 0x03 0x00 0x00 0x00 0x00 0x01 0x23 0x45 + 0xc8 0x03 0xff 0xff 0xff 0xff 0x12 0x34 0x50 + +The former encoding puts a leading 0 in the first byte and uses exponent +0, the latter encoding directly starts putting two decimal digits in one +byte and then in the end has to "erase" the trailing 0 by using exponent +-1, encoded by the 4 byte sequence 0xff 0xff 0xff 0xff. + +There for the unholy nibble problem is solved and parsing (and indeed +dumping) can be efficient. + + +## Tagging + +Types 0xee-0xef are used for tagging of values to implement logical +types. + +For example, if type 0x1c did not exist, the database driver could +serialize a timestamp object (Date in JavaScript, Instant in Java, etc) +into a Unix timestamp, a 64-bit integer. Assuming the lack of schema, +upon deserialization it would not be possible to tell an integer from +a timestamp and deserialize the value accordingly. + +Type tagging resolves this by attaching an integer tag to values that +can then be read when deserializing the value, e.g. that tag=1 is a +timestamp and the relevant timestamp class should be used. + +The tag values are specified separately and applications can also +specify their own to have the database driver deserialize their specific +data types into the appropriate classes (including models). + +Essentially this is object-relational mapping for parts of documents. + +The format of the type is: + + 0xee + TAG number in 1 byte + sub VPack value + +or + + 0xef + TAG number in 8 bytes, little-endian encoding + sub VPack value + + +## Custom types + +Note that custom types should usually not be used for data exchange but +only internally in systems. Nevertheless, the design of this part of +the specification is made such that it is possible by generic methods +to derive the byte length of each custom data type. + +The following user-defined types exist: + + - 0xf0 : 1 byte payload, directly following the type byte + - 0xf1 : 2 bytes payload, directly following the type byte + - 0xf2 : 4 bytes payload, directly following the type byte + - 0xf3 : 8 bytes payload, directly following the type byte + - 0xf4-0xf6 : length of the payload is described by a single further + unsigned byte directly following the type byte, the + payload of that many bytes follows + - 0xf7-0xf9 : length of the payload is described by two bytes (little + endian unsigned integer) directly following the type + byte, the payload of that many bytes follows + - 0xfa-0xfc : length of the payload is described by four bytes (little + endian unsigned integer) directly following the type + byte, the payload of that many bytes follows + - 0xfd-0xff : length of the payload is described by eight bytes (little + endian unsigned integer) directly following the type + byte, the payload of that many bytes follows + +Note: In types 0xf4 to 0xff the "payload" refers to the actual data not +including the length specification. diff --git a/VelocyPack_zh.md b/VelocyPack_zh.md new file mode 100644 index 0000000..1e2da2b --- /dev/null +++ b/VelocyPack_zh.md @@ -0,0 +1,323 @@ +# VelocyPack(VPack) + Version 1 + VelocyPack(VPack)是一种快速而紧凑的序列化格式 + +# 共性 + VPack是面向(无符号)字节的,因此VPack值只是字节序列,并且与平台无关。值不一定要对齐,因此必须正确组织对较大子值的所有访问,以避免CPU的对齐假设。 + +# 值类型 + 我们描述了一个VPack值,该值本质上是递归的,但驻留在一个连续的内存块中(有两个例外,请参见下文)。假设该值从地址A开始,则第一个字节V指示当前VPack值的类型(通常是长度): + +# 总述 我们首先给出一个简短而准确的概述作为参考,以供参考,有关数组和对象的详细信息,请参见下文: + 0x00:0 无-表示不存在任何类型和值,VPack值中不允许 + 0x01:1 空数组 + 0x02:2 不带索引表的数组(所有子项具有相同的字节长度),1字节字节长度 + 0x03:3 不带索引表的数组(所有子项具有相同的字节长度),2字节字节长度 + 0x04:4 不带索引表的数组(所有子项具有相同的字节长),4字节字节长 + 0x05:5 不带索引表的数组(所有子项具有相同的字节长度),8字节字节长度 + 0x06:6 具有1字节索引表偏移量,bytelen和#个子区间的数组 + 0x07:7 具有2字节索引表偏移量,bytelen和#个子区间的数组 + 0x08:8 具有4字节索引表偏移量,bytelen和#个子区间的数组 + 0x09:9 具有8字节索引表偏移量,bytelen和#个子区间的数组 + 0x0a:10 空对象 + 0x0b:11 具有1字节索引表偏移量的对象,按属性名称排序,1字节bytelen和#个子值 + 0x0c:12 具有2字节索引表偏移量的对象,按属性名称排序,2字节bytelen和#个子值 + 0x0d:13 具有4字节索引表偏移量的对象,按属性名称排序,4字节bytelen和#个子值 + 0x0e:14 具有8字节索引表偏移量的对象,按属性名称排序,8字节bytelen和#个子值 + 0x0f:15 具有1字节索引表偏移量的对象,未按属性名称排序,1字节bytelen和#个子值 + 0x10:16 具有2字节索引表偏移量的对象,未按属性名称排序,2字节bytelen和#个子值 + 0x11:17 具有4字节索引表偏移量的对象,未按属性名称排序,4字节bytelen和#个子值 + 0x12:18 具有8字节索引表偏移量的对象,未按属性名称排序,8字节字节数和#个子值 + 0x13:19 紧凑数组,没有索引表 + 0x14:20 紧凑对象,没有索引表 + 0x15-0x16:21-22保留 + 0x17:23 不合法-此类型可用于指示嵌入应用程序中不合法的值 + 0x18:24 null + 0x19:25 false + 0x1a:26 true + 0x1b:27 双IEEE-754,后跟8个字节,存储为与uint64等效的小字节序 + 0x1c:28 UTC日期(自纪元以来),以8字节有符号int,little endian,二进制补码形式存储 + 0x1d:29 外部(仅在内存中):一个char *,指向另一个VPack项在内存中的实际位置,磁盘或网络上的VPack值中不允许 + 0x1e:30 minKey,比较所有其他值<的无意义的值 + 0x1f:31 maxKey,与所有其他值相比>的无意义值 + 0x20-0x27:32-39带符号的int,小字节序,1至8个字节,数字为V-0x1f,二进制补码 + 0x28-0x2f:40-47 uint,小端,1至8字节,数字为V-0x27 + 0x30-0x39:48-57 ,小整数0,1,... 9 + 0x3a-0x3f:58- 63 小负整数-6,-5,...,-1 + 0x40-0xbe:64- 190 UTF-8字符串,使用V-0x40字节(不是Unicode字符!),长度为0,所以0x40是空字符串,最大长度为126,请注意,这里的字符串不是以0结尾的,并且可以包含NUL个字节 + 0xbf:191 长UTF-8字符串,接下来的8个字节是字符串的长度(以字节为单位)(不是Unicode字符),是小端无符号整数,请注意,长字符串不以0结尾,并且可以包含NUL字节 + 0xc0-0xc7:192-199 二进制blob,下一个V-0xbf字节是blob的长度(以字节为单位),请注意二进制blob不会以零结尾 + 0xc8-0xcf:200-207 正长打包BCD编码的浮点数,后跟V-0xc7字节,以一点尾数方式对尾数的长度(以字节为单位)进行编码。之后紧随其后的是4个字节(乘以10的幂),然后乘以尾数,将其存储为小尾数2的补码有符号32位整数。 + 之后,跟着指定的开始处的长度信息一样多的字节,每个字节以大端字节序打包的BCD编码两位。 + 示例:12345十进制可以编码为0xc8 0x03 0x00 0x00 0x00 0x00 0x01 0x23 0x45或0xc8 0x03 0xff 0xff 0xff 0xff 0x12 0x34 0x50 + 0xd0-0xd7:208- 215 负长打包BCD编码的浮点数,后跟V-0xcf字节,以一点尾数法对尾数的长度(以字节为单位)进行编码。之后,与上面的正长打包BCD编码浮点相同。 + 0xd8-0xed:216-237 保留 + 0xee-0xef:238-239 逻辑类型的值标记 + 0xf0-0xff:240-255 自定义类型 + +# 详述 +## Arrays + 空数组只是一个字节0x01。 + 接下来,我们将描述类型为0x02到0x09的情况,请参见下面的特殊紧凑型0x13。 + + 非空数组看起来像以下之一: + + one of 0x02 to 0x05 + BYTELENGTH + OPTIONAL UNUSED: padding + sub VPack values + 要么 + + 0x06 + BYTELENGTH in 1 byte + NRITEMS in 1 byte + OPTIONAL UNUSED: 6 bytes of padding + sub VPack values + INDEXTABLE with 1 byte per entry + 要么 + + 0x07 + BYTELENGTH in 2 bytes + NRITEMS in 2 bytes + OPTIONAL UNUSED: 4 bytes of padding + sub VPack values + INDEXTABLE with 4 byte per entry + 要么 + + 0x08 + BYTELENGTH in 4 bytes + NRITEMS in 4 bytes + sub VPack values + INDEXTABLE with 4 byte per entry + 要么 + + 0x09 + BYTELENGTH in 8 bytes + sub VPack values + INDEXTABLE with 8 byte per entry + NRITEMS in 8 bytes + 如果类型允许使用任何可选的填充,则填充必须完全由填充字节的长度,BYTELENGTH的长度和NRITEMS的长度(如果存在)之和等于8的字节数组成。如果BYTELENGTH的长度为已经是8,不允许填充。整个填充必须由零字节(ASCII NUL)组成。 + + 数字(对于字节长度,INDEXTABLE中的子值数量和偏移量)是小端无符号整数,对于类型0x02和0x06使用1字节,对于类型0x03和0x07使用2字节,对于类型0x04和0x08使用4字节,对于类型0x05和0x09使用8字节。 + + NRITEMS是如上所述的单个数字。 + + INDEXTABLE包含: + + 对于类型0x06-0x09,偏移量数组(未对齐,采用上述数字格式)较早的偏移量位于较低地址。偏移量从VPack值的开头开始测量。 + 类型为0x06到0x09的非空数组具有一个小的标头,包括它们的字节长度,子值数量,所有子值以及最后一个包含这些子值偏移量的索引表。要找到索引表,请先考虑子值的数量,然后是末尾,再从索引表的底部开始,并考虑其条目的宽度。 + + 对于类型0x02至0x05,没有偏移表,也没有项目数。第一项根据字节长度字段的类型和宽度而定,从地址A + 2,A + 3,A + 5或分别为A + 9开始。请注意以下特殊规则:在填充零字节之后,允许第一个子值的实际位置再退一步。 + + 例如,如果两个字节长度(BYTELENGTH)都使用了2个字节,则随后可以选择填充4个零字节,并且实际的VPack子值可以从A + 9开始。这是为了给构建VPack值的程序提供在开始时保留8个字节的机会,直到以后才发现可以写出字节长度的字节更少。可以通过找到第一个子值,其字节长度并将可用空间除以它来确定子值的数量。 + + 对于类型0x06至0x09,偏移量表描述了子值所在的位置。子值不必在“子值数”字段之后立即开始。 + + 如上所述,允许包括可选的填充。同样在这里,任何填充都必须由连续的零字节(ASCII NUL)组成,并且填充长度必须足以填充BYTELENGTH的长度和NRITEMS的长度为8。 + + 例如,如果BYTELENGTH和NRITEMS都可以用2个字节表示,则它们的长度之和为4。因此,允许在此处添加4个字节的填充,以便第一个子值可以位于地址A + 9。 + + 对于8字节数字情况(类型0x05),有一个例外:在这种情况下,元素数被移到索引表的后面。当一个人在开始时已经保留了8个字节,后来又注意到字节长度需要全部8个字节时,这是为了不移动内存而逃脱。在这种情况下,不允许包含任何填充。 + + 所有偏移量均从基数A开始测量。 + + 范例: + + [1,2,3] 有十六进制转储 + + 02 05 31 32 33 + 以最紧凑的形式表示,但以下情况同样可行,尽管不一定建议使用: + + 例子: + + 03 06 00 31 32 33 + + 04 08 00 00 00 31 32 33 + + 05 0c 00 00 00 00 00 00 00 31 32 33 + + 06 09 03 31 32 33 03 04 05 + + 07 0e 00 03 00 31 32 33 05 00 06 00 07 00 + + 08 18 00 00 00 03 00 00 00 31 32 33 09 00 00 00 0a 00 00 00 0b 00 00 00 + + 09 + 2c 00 00 00 00 00 00 00 + 31 32 33 + 09 00 00 00 00 00 00 00 + 0a 00 00 00 00 00 00 00 + 0b 00 00 00 00 00 00 00 + 03 00 00 00 00 00 00 00 + 请注意,不建议以太长的格式编码短数组。 + + 现在我们描述特殊类型0x13,它对于特别紧凑的数组表示很有用。请注意,在某种程度上这与VelocyPack格式的原理背道而驰,因为不再能够快速访问子值,因此必须扫描数组中的所有项以找到特定项。但是,VelocyPack的某些用例只需要顺序访问(例如JSON转储),并且对紧凑性有特殊的需求。 + + 此数组类型的整体格式为 + + 0x13作为类型字节BYTELENGTH子VPack值NRITEMS + + 尽管子VelocyPack值可以具有不同的字节大小,但根本没有索引表。BYTELENGTH和NRITEMS以特殊格式编码,我们现在将对其进行描述。 + + BYTELENGTH由1到8个字节组成,除最后一个字节外,所有字节均已设置其高位。因此,高位确定实际使用了多少个字节。所有这些位的低7位以一点字节序的形式一起构成了实际的字节长度。也就是说,地址A + 1处的字节包含字节长度的最低有效7位(0至6),地址A + 2之后的字节包含位7至13,依此类推。由于字节的总数限制为8,因此可以对多达56位的无符号整数进行编码,这是此类紧凑数组表示形式的大小的总体限制。 + + NRITEMS条目的编码方式基本上相同,只是它以相反的顺序排列在内存中。也就是说,必须使用BYTELENGTH来查找数组值的末尾并返回字节,直到找到高复位位的字节为止。最后一个字节(在最高的存储器地址处)包含NRITEMS值的最低有效7位,后一个7至13位,依此类推。 + + 这是一个示例,可以将数组[1,16]编码如下: + + 13 06 + 31 28 10 + 02 +## Objects + 空对象只是一个字节0x0a。 + + 接下来,我们描述类型为0x0b到0x12的情况,有关特殊的紧凑型0x14,请参见下文。 + + 非空对象如下所示: + + one of 0x0b - 0x12 + BYTELENGTH + optional NRITEMS + sub VPack values as pairs of attribute and value + optional INDEXTABLE + NRITEMS for the 8-byte case + + 数字(对于字节长度,INDEXTABLE中的子值数量和偏移量)是小端无符号整数,对于类型0x0b和0x0f使用1个字节,对于类型0x0c和0x10使用2个字节,对于类型0x0d和0x11使用4个字节,对于类型8x类型0x0e和0x12。 + + NRITEMS是如上所述的单个数字。 + + INDEXTABLE包含: + 较早的偏移量数组(未对齐,采用上述数字格式)位于较低的地址。偏移量是从VPack值的开头开始测量的。 + 非空对象的标头很小,包括字节长度,子值数量,所有子值以及最后一个包含子值偏移量的索引表。要查找索引表,请先考虑子值的数量,然后查找末尾,再从索引表的底部开始,考虑其条目的宽度。 + + 对于所有类型,偏移量表都描述了子值所在的位置。子值不必在“子值数”字段之后立即开始。出于性能原因,在构建值时,可能希望为字节长度和子值的数量保留8个字节,而不填补空白,即使后来发现偏移量(因此字节长度仅使用2个字节)也是如此。 。 + + 有一种特殊情况:空对象仅存储为单个字节0x0a。 + + 还有一个例外:对于8字节数字(0x12),子值的数量存储在INDEXTABLE的后面。当一个人在开始时已经保留了8个字节,后来又注意到字节长度需要全部8个字节时,这是为了不移动内存而逃脱。 + + 所有偏移量均从基数A开始测量。 + + 每个条目都由键和值两部分组成,它们如上所述被编码为普通的VPack值,第一个始终是长或短的UTF-8字符串,从字节0x40-0xbf开始,如下所述。第二个是任何其他VPack值。 + + 有一个扩展:对于密钥,可以使用正的小整数值0x30-0x39或以类型字节0x28-0x2f开头的无符号整数。任何此类整数值都是属性名称的外部表的索引。当仅出现很少的属性名称或经常重复某些属性名称时,这些方法很方便。编码此类属性名称表的标准方法是使用此处指定的VPack字符串数组。 + + 对象总是存储有排序的键/值对,并按每个嵌套级别上键的按字节比较排序。排序有一些开销,但允许在以后的对数时间内查找键。注意,仅索引表需要排序,不需要这些表中的偏移量增加。由于索引表位于实际的子值之后,因此可以通过线性写入来构建复杂的VPack值。 + + 示例:对象{"a": 12, "b": true, "c": "xyz"}可以具有hexdump: + + 0b + 13 03 + 41 62 1a + 41 61 28 0c + 41 63 43 78 79 7a + 06 03 0a + 可以使用具有更长条目的索引表来完成相同的对象,如以下示例所示: + + 0d + 22 00 00 00 + 03 00 00 00 + 41 62 1a + 41 61 28 0c + 41 63 43 78 79 7a + 0c 00 00 00 09 00 00 00 10 00 00 00 + 类似地,对于类型0x0c和2个字节的偏移量,字节长度和子值数量,或者对于类型0x0e和8个字节的数字。 + + 请注意,不建议对索引表太长的短对象进行编码。 + + 特殊的紧凑物体 + 现在我们描述特殊类型0x14,它对于特别紧凑的对象表示很有用。请注意,在某种程度上这与VelocyPack格式的原理背道而驰,因为不再能够快速访问子值,因此必须扫描对象中的所有键/值对以找到特定的键/值对。但是,VelocyPack的某些用例只需要顺序访问(例如JSON转储),并且对紧凑性有特殊的需求。 + + 该对象类型的整体格式为 + + 0x14作为字节BYTELENGTH子VPack键/值对的类型字节NRPAIRS + + 尽管子VelocyPack值可以具有不同的字节大小,但根本没有索引表。BYTELENGTH和NRPAIRS以特殊格式编码,我们现在将对其进行描述。它与特殊紧凑型数组0x13相同,为完整起见,在此重复。 + + BYTELENGTH由1到8个字节组成,除最后一个字节外,所有字节均已设置其高位。因此,高位确定实际使用了多少个字节。所有这些位的低7位以一点字节序的形式一起构成了实际的字节长度。也就是说,地址A + 1处的字节包含字节长度的最低有效7位(0至6),地址A + 2之后的字节包含位7至13,依此类推。由于字节的总数限制为8,因此可以对多达56位的无符号整数进行编码,这是此类紧凑数组表示形式的大小的总体限制。 + + NRPAIRS条目的编码方式基本上相同,只是它在内存中的排列顺序相反。也就是说,必须使用BYTELENGTH来查找数组值的末尾并返回字节,直到找到高复位位的字节为止。最后一个字节(在最高的存储器地址处)包含NRPAIRS值的最低有效7位,后一个7至13位,依此类推。 + + 这是一个示例,对象{“ a”:1,“ b”:16}可以编码如下: + + 14 0a + 41 61 31 42 62 28 10 + 02 +## Doubles + 类型0x1b使用类型字节后的8个字节指示双IEEE-754值。为了保证平台独立性,字节顺序的详细信息如下。通过使用memcpy将内部double值复制到uint64_t来完成编码。然后,将此64位无符号整数存储为VPack值中的8位小字节序。解码的方向相反。实际上,这应该整理出IEEE-754中未确定的字节顺序。 + +## Dates + 类型0x1c指示在类型之后紧接着以8字节小尾数补码表示的有符号64位整数。该值表示自该时期以来的通用UTC时间(以毫秒为单位),该时间是1970年1月1日UTC的00:00。 + +## External VPack values + 此类型仅用于内存中,而不用于通过磁盘或网络进行数据交换。因此,我们仅需要指定以下k个字节为当前体系结构上char *的memcpy。该char *指向内存中其他位置的实际VPack值。 + +## Artifical minimal and maximal keys + 这些类型0x1e和0x1f的值除了分别比较小于或大于任何其他VPack值之外没有其他意义。这个想法是可以在定义所有VPack值的总顺序以指定无限间隔的左端或右端的系统中使用它们。 + +## Integer types + 有多种指定整数的方法。对于-6到9的小数值(包括-6)(包括0x30到0x3f),可以在单个字节中存储。此后,可以在字节类型中对有符号和无符号整数类型进行编码,这些字节类型使用的字节数(有符号的范围为0x20-0x27,无符号的范围为0x28-0x2f)。 + +## Null and boolean values + 这三个值使用单个字节存储相应的JSON值。 + +## Binary data + 字符串存储为UTF-8编码的字节序列。有两种变体,短的和长的。简短地说,字节长度(不是UTF-8字符的数量)直接在该类型中编码,并且可以工作至字节长度126,包括字节长度126。为此使用类型0x40至0xbe,字节长度为V -0x3f(如果V是类型字节)。对于长度超过126个字节的字符串,类型字节为0xbf,并且字符串的字节长度使用小尾数无符号整数表示形式存储在类型字节之后的前8个字节中。实际的字符串在这8个字节之后。两种情况都没有终止的零字节,并且字符串可能包含零字节。 + +## Binary data + 字节类型0xc0至0xc7允许将任意二进制字节序列存储为VPack值。格式如下:如果V是类型字节,则V-0xbf字节跟在其后,以一个小端无符号整数表示二进制数据的长度,该整数紧随这些长度字节之后。不保证对齐。内容完全取决于用户。 + +## Packed BCD long floats + 这些类型用于表示任意精度的十进制数字。正数和负数有不同的类型。这些值的整体格式为: + + one of 0xc8 - 0xcf (positive) or of 0xd0 - 0xd7 (negative) + LENGTH OF MANTISSA in bytes + EXPONENT (as 4-byte little endian signed two's complement integer) + MANTISSA (as packed BCD-encoded integer, big-endian) + 字节类型描述数字的符号以及用于指定尾数字节长度的字节数。通常,如果V是类型字节,则将V-0xc7(在正数情况下)或V-0xcf(在负数情况下)字节用作尾数的长度,并在该字节后直接存储为小端无符号整数长度。在此之后,正好跟随4个字节(小尾数有符号二进制补码整数)来指定指数。指数之后,是实际的尾数字节。 + + 使用压缩的BCD,以便每个字节正好存储2个十进制数字,如0x34中的十进制数字34一样。因此,尾数始终为偶数个十进制数字。请注意,尾数以大尾数形式存储,以提高解析和转储效率。这导致“邪恶的半字节问题”:当JSON解析器看到一个长数字的开头时,它不知道后面是偶数还是奇数。但是,出于效率原因,它希望在读取输入时开始将字节写入输出。这就是救援的关键所在,如以下示例所示: + + 12345 decimal can be encoded as: + + 0xc8 0x03 0x00 0x00 0x00 0x00 0x01 0x23 0x45 + 0xc8 0x03 0xff 0xff 0xff 0xff 0x12 0x34 0x50 + 前一种编码在第一个字节中放置前导0并使用指数0,后一种编码直接开始在一个字节中放置两个十进制数字,然后最后必须使用由-1编码的指数-1“擦除”尾随0。 4字节序列0xff 0xff 0xff 0xff。 + + 在那里解决了邪恶的蚕食问题,并且解析(实际上是转储)效率很高。 + +## Tagging + 类型0xee-0xef用于标记值以实现逻辑类型。 + + 例如,如果类型0x1c不存在,则数据库驱动程序可以将时间戳对象(JavaScript中的Date,Java中的Instant等)序列化为Unix时间戳(64位整数)。假设缺少模式,则在反序列化时,不可能从时间戳中分辨出整数并相应地反序列化该值。 + + 类型标记通过将整数标记附加到值上来解决此问题,这些值可在反序列化值时读取,例如,tag = 1是时间戳,应使用相关的时间戳类。 + + 标记值是分别指定的,应用程序也可以指定它们自己的名称,以使数据库驱动程序将其特定数据类型反序列化为适当的类(包括模型)。 + + 本质上,这是文档各部分的对象关系映射。 + + 类型的格式为: + + 0xee + TAG number in 1 byte + sub VPack value + 要么 + + 0xef + TAG number in 8 bytes, little-endian encoding + sub VPack value +## Custom types + 请注意,自定义类型通常不应用于数据交换,而只能用于系统内部。尽管如此,本规范的这一部分仍进行了设计,使得可以通过通用方法得出每种自定义数据类型的字节长度。 + + 存在以下用户定义的类型: + + 0xf0:1字节有效负载,紧随类型字节之后 + 0xf1:2字节有效负载,紧随类型字节之后 + 0xf2:4字节有效负载,紧随类型字节之后 + 0xf3:8字节有效负载,紧随类型字节之后 + 0xf4-0xf6:有效载荷的长度由紧随类型字节之后的另一个无符号字节描述,该多个字节的有效载荷如下 + 0xf7-0xf9:有效负载的长度由紧随类型字节之后的两个字节(小尾数无符号整数)描述,该字节的有效负载紧随其后 + 0xfa-0xfc:有效载荷的长度由紧随类型字节之后的四个字节(小尾数无符号整数)描述,该字节的有效载荷如下 + 0xfd-0xff:有效载荷的长度由紧随类型字节之后的八个字节(小尾数无符号整数)描述,该字节的有效载荷如下 + 注意:在类型0xf4至0xff中,“有效负载”是指不包括长度说明的实际数据。 \ No newline at end of file diff --git a/include/eVPack.hrl b/include/eVPack.hrl new file mode 100644 index 0000000..1e197da --- /dev/null +++ b/include/eVPack.hrl @@ -0,0 +1,19 @@ +-type vpack() :: binary() | iodata(). +-type vpOpt() :: pos_integer(). +-export_type([vpack/0, vpOpt/0]). + +-define(vpObjNcNs, 0). %% 不压缩编码 不排序Key +-define(vpObjNcYs, 1). %% 不压缩编码 排序Key +-define(vpObjYc, 2). %% 压缩编码 + +-define(vpArrNc, 0). %% 不压缩编码 +-define(vpArrYc, 1). %% 压缩编码 + +-define(VpDefObjOpt, 0). %% 默认选项 不排序Obj key Obj不压缩 +-define(VpDefArrOpt, 0). %% 默认选项 Arr不压缩 + +-define(VpAllOpts(Arr, Obj), Obj bsl 1 bor Arr). %% 拼装Obj 与 Arr选项 +-define(VpObjOpts(VpAllOpts), VpAllOpts bsr 1). %% 获取Obj选项 +-define(VpArrOpts(VpAllOpts), VpAllOpts band 1). %% 获取Arr选项 + +-define(VpBinaryCopyRatio, 1.2). \ No newline at end of file diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..c5e5045 --- /dev/null +++ b/rebar.config @@ -0,0 +1,4 @@ +{erl_opts, [debug_info, {i, "include"}]}. +{deps, [ + {erlSync, ".*", {git, "http://47.108.26.175:53000/SisMaker/erlSync.git", {branch, "master"}}} +]}. diff --git a/src/decoderTest.erl b/src/decoderTest.erl new file mode 100644 index 0000000..d0b0be5 --- /dev/null +++ b/src/decoderTest.erl @@ -0,0 +1,230 @@ +-module(decoderTest). + +-compile([no_auto_import]). + +-export([ + test/0 +]). + +test() -> + try do() of + _ -> + ok + catch + E:C:S -> + {E:C:S} + end. + +do() -> + [] = erlVPack:decode(<<1>>), + #{} = erlVPack:decode(<<10>>), + illegal = erlVPack:decode(<<23>>), + nil = erlVPack:decode(<<24>>), + false = erlVPack:decode(<<25>>), + true = erlVPack:decode(<<26>>), + 1.33699999999999988631e+2 = erlVPack:decode(<<27, 102, 102, 102, 102, 102, 182, 96, 64>>), + -1.33699999999999988631e+2 = erlVPack:decode(<<27, 102, 102, 102, 102, 102, 182, 96, 192>>), + 609976800000 = erlVPack:decode(<<28, 0, 83, 115, 5, (-114), 0, 0, 0>>), + min_key = erlVPack:decode(<<30>>), + max_key = erlVPack:decode(<<31>>), + + 0 = erlVPack:decode(<<48>>), + 1 = erlVPack:decode(<<49>>), + 2 = erlVPack:decode(<<50>>), + 3 = erlVPack:decode(<<51>>), + 4 = erlVPack:decode(<<52>>), + 5 = erlVPack:decode(<<53>>), + 6 = erlVPack:decode(<<54>>), + 7 = erlVPack:decode(<<55>>), + 8 = erlVPack:decode(<<56>>), + 9 = erlVPack:decode(<<57>>), + -6 = erlVPack:decode(<<58>>), + -5 = erlVPack:decode(<<59>>), + -4 = erlVPack:decode(<<60>>), + -3 = erlVPack:decode(<<61>>), + -2 = erlVPack:decode(<<62>>), + -1 = erlVPack:decode(<<63>>), + -1 = erlVPack:decode(<<32, 255>>), + 127 = erlVPack:decode(<<32, 127>>), + -128 = erlVPack:decode(<<32, 128>>), + -1 = erlVPack:decode(<<33, 255, 255>>), + -1 = erlVPack:decode(<<34, 255, 255, 255>>), + -1 = erlVPack:decode(<<35, 255, 255, 255, 255>>), + -1 = erlVPack:decode(<<36, 255, 255, 255, 255, 255>>), + -1 = erlVPack:decode(<<37, 255, 255, 255, 255, 255, 255>>), + -1 = erlVPack:decode(<<38, 255, 255, 255, 255, 255, 255, 255>>), + -1 = erlVPack:decode(<<39, 255, 255, 255, 255, 255, 255, 255, 255>>), + 9223372036854775807 = erlVPack:decode(<<39, 255, 255, 255, 255, 255, 255, 255, 127>>), + 255 = erlVPack:decode(<<40, 255>>), + 65535 = erlVPack:decode(<<41, 255, 255>>), + 16777215 = erlVPack:decode(<<42, 255, 255, 255>>), + 4294967295 = erlVPack:decode(<<43, 255, 255, 255, 255>>), + 1099511627775 = erlVPack:decode(<<44, 255, 255, 255, 255, 255>>), + 281474976710655 = erlVPack:decode(<<45, 255, 255, 255, 255, 255, 255>>), + 72057594037927935 = erlVPack:decode(<<46, 255, 255, 255, 255, 255, 255, 255>>), + 18446744073709551615 = erlVPack:decode(<<47, 255, 255, 255, 255, 255, 255, 255, 255>>), + <<"Hallo Welt!">> = erlVPack:decode(<<75, 72, 97, 108, 108, 111, 32, 87, 101, 108, 116, 33>>), + <<"Hello World!">> = erlVPack:decode(<<76, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33>>), + Str = + <<"Lorem ipsum dolor sit amet, consectetuer " + "adipiscing elit. Aenean commodo ligula " + "eget dolor. ", + "Aenean massa. Cum sociis natoque penatibus " + "et magnis dis parturient montes, nascetur " + "ridiculus mus. ", + "Donec quam felis, ultricies nec, pellentesque " + "eu, pretium quis, sem. Nulla consequat " + "massa quis enim. ", + "Donec pede justo, fringilla vel, aliquet " + "nec, vulputate eget, arcu. In enim justo, " + "rhoncus ut, imperdiet a, ", + "venenatis vitae, justo. Nullam dictum " + "felis eu pede mollis pretium. Integer " + "tincidunt. Cras dapibus. ", + "Vivamus elementum semper nisi. Aenean " + "vulputate eleifend tellus.">>, + StrBin = <<191, 55, 2, 0, 0, 0, 0, 0, 0, 76, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109, 32, 100, 111, 108, 111, 114, 32, 115, + 105, 116, 32, 97, 109, 101, 116, 44, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 32, 97, 100, + 105, 112, 105, 115, 99, 105, 110, 103, 32, 101, 108, 105, 116, 46, 32, 65, 101, 110, 101, 97, 110, 32, 99, 111, + 109, 109, 111, 100, 111, 32, 108, 105, 103, 117, 108, 97, 32, 101, 103, 101, 116, 32, 100, 111, 108, 111, 114, 46, + 32, 65, 101, 110, 101, 97, 110, 32, 109, 97, 115, 115, 97, 46, 32, 67, 117, 109, 32, 115, 111, 99, 105, 105, + 115, 32, 110, 97, 116, 111, 113, 117, 101, 32, 112, 101, 110, 97, 116, 105, 98, 117, 115, 32, 101, 116, 32, 109, + 97, 103, 110, 105, 115, 32, 100, 105, 115, 32, 112, 97, 114, 116, 117, 114, 105, 101, 110, 116, 32, 109, 111, 110, + 116, 101, 115, 44, 32, 110, 97, 115, 99, 101, 116, 117, 114, 32, 114, 105, 100, 105, 99, 117, 108, 117, 115, 32, + 109, 117, 115, 46, 32, 68, 111, 110, 101, 99, 32, 113, 117, 97, 109, 32, 102, 101, 108, 105, 115, 44, 32, 117, 108, 116, 114, 105, + 99, 105, 101, 115, 32, 110, 101, 99, 44, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 101, + 117, 44, 32, 112, 114, 101, 116, 105, 117, 109, 32, 113, 117, 105, 115, 44, 32, 115, 101, 109, 46, 32, 78, 117, + 108, 108, 97, 32, 99, 111, 110, 115, 101, 113, 117, 97, 116, 32, 109, 97, 115, 115, 97, 32, 113, 117, 105, 115, 32, 101, 110, 105, + 109, 46, 32, 68, 111, 110, 101, 99, 32, 112, 101, 100, 101, 32, 106, 117, 115, 116, 111, 44, 32, 102, 114, 105, 110, 103, 105, 108, + 108, 97, 32, 118, 101, 108, 44, 32, 97, 108, 105, 113, 117, 101, 116, 32, 110, 101, 99, 44, 32, 118, 117, 108, + 112, 117, 116, 97, 116, 101, 32, 101, 103, 101, 116, 44, 32, 97, 114, 99, 117, 46, 32, 73, 110, 32, 101, 110, + 105, 109, 32, 106, 117, 115, 116, 111, 44, 32, 114, 104, 111, 110, 99, 117, 115, 32, 117, 116, 44, 32, 105, 109, 112, 101, 114, 100, + 105, 101, 116, 32, 97, 44, 32, 118, 101, 110, 101, 110, 97, 116, 105, 115, 32, 118, 105, 116, 97, 101, 44, 32, 106, 117, 115, 116, + 111, 46, 32, 78, 117, 108, 108, 97, 109, 32, 100, 105, 99, 116, 117, 109, 32, 102, 101, 108, 105, 115, 32, 101, + 117, 32, 112, 101, 100, 101, 32, 109, 111, 108, 108, 105, 115, 32, 112, 114, 101, 116, 105, 117, 109, 46, 32, 73, + 110, 116, 101, 103, 101, 114, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 46, 32, 67, 114, 97, 115, 32, 100, + 97, 112, 105, 98, 117, 115, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 101, 108, 101, 109, 101, 110, 116, 117, + 109, 32, 115, 101, 109, 112, 101, 114, 32, 110, 105, 115, 105, 46, 32, 65, 101, 110, 101, 97, 110, 32, 118, 117, + 108, 112, 117, 116, 97, 116, 101, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, 116, 101, 108, 108, 117, 115, 46>>, + Str = erlVPack:decode(StrBin), + ExBin = erlVPack:decode(<<49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<192, 9, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<193, 9, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<194, 9, 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<195, 9, 0, 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<196, 9, 0, 0, 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<197, 9, 0, 0, 0, 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<198, 9, 0, 0, 0, 0, 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = erlVPack:decode(<<199, 9, 0, 0, 0, 0, 0, 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57>>), + ExBin = [1, 2, 3], + Ex3 = erlVPack:decode(<<6, 9, 3, 49, 50, 51, 3, 4, 5>>), + Ex3 = erlVPack:decode(<<6, 10, 3, 0, 49, 50, 51, 3, 4, 5>>), + Ex3 = erlVPack:decode(<<7, 14, 0, 3, 0, 49, 50, 51, 5, 0, 6, 0, 7, 0>>), + Ex3 = erlVPack:decode(<<8, 24, 0, 0, 0, 3, 0, 0, 0, 49, 50, 51, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0>>), + Ex3 = erlVPack:decode(<<9, 44, 0, 0, 0, 0, 0, 0, 0, 49, 50, 51, 9, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0>>), + Ex3 = erlVPack:decode(<<6, 5, 1, 49, 3>>), + Ex3 = [1], + Ex4 = [1, 2, 3], + Ex4 = erlVPack:decode(<<2, 5, 49, 50, 51>>), + Ex4 = erlVPack:decode(<<2, 6, 0, 49, 50, 51>>), + Ex4 = erlVPack:decode(<<3, 6, 0, 49, 50, 51>>), + Ex4 = erlVPack:decode(<<4, 8, 0, 0, 0, 49, 50, 51>>), + Ex4 = erlVPack:decode(<<5, 12, 0, 0, 0, 0, 0, 0, 0, 49, 50, 51>>), + [0.0] = erlVPack:decode(<<2, 11, 27, 0:64/integer-little-unsigned>>), + 0 = erlVPack:decode(<<2, 11, 28, 0:64/integer-little-unsigned>>), + [1, 16] = erlVPack:decode(<<19, 6, 49, 40, 16, 2>>), + Ex5 = [[1, 2, 3], [1, 2, 3]], + Ex5 = erlVPack:decode(<<2, 12, 2, 5, 49, 50, 51, 2, 5, 49, 50, 51>>), + Ex6 = [[1, 2, 3], [1, 2, 3]], + Ex6 = erlVPack:decode(<<2, 14, 19, 6, 49, 50, 51, 3, 19, 6, 49, 50, 51, 3>>), + Ex7 = #{<<"a">> => <<"b">>}, + Ex7 = erlVPack:decode(<<11, 8, 1, 65, 97, 65, 98, 3>>), + Ex8 = #{<<"a">> => 12, <<"b">> => true, <<"c">> => <<"xyz">>}, + Ex8 = erlVPack:decode(<<20, 16, 65, 97, 40, 12, 65, 98, 26, 65, 99, 67, 120, 121, 122, 3>>), + Ex9 = [#{<<"a">> => 12, <<"b">> => true, <<"c">> => <<"xyz">>}, #{<<"a">> => 12, <<"b">> => true, <<"c">> => <<"xyz">>}], + Ex9 = erlVPack:decode(<<19, 35, 20, 16, 65, 97, 40, 12, 65, 98, 26, 65, 99, 67, 120, 121, 122, 3, 20, 16, 65, 97, 40, 12, 65, 98, 26, 65, 99, 67, 120, 121, 122, 3, 2>>), + Ex10 = [#{<<"key">> => 42}, <<"fooooobar">>, <<"x">>, <<1, 2, 3, 4, 5, 6, 7, 8>>], + Ex10Bin = <<2, 42, 11, 10, 1, 67, 107, 101, 121, 40, 42, 3, 73, 102, 111, 111, 111, 111, 111, + 98, 97, 114, 191, 1, 0, 0, 0, 0, 0, 0, 0, 120, 192, 8, 1, 2, 3, 4, 5, 6, 7, 8>>, + Ex10 = erlVPack:decode(Ex10Bin), + _Ex11 = #{<<"0">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"1">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"2">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"3">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"4">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}}, + + _Ex12 = #{<<"0">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"1">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"2">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"3">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"4">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"5">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"6">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"7">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"8">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"9">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}}. diff --git a/src/eVPack.app.src b/src/eVPack.app.src new file mode 100644 index 0000000..0eac770 --- /dev/null +++ b/src/eVPack.app.src @@ -0,0 +1,14 @@ +{application, eVPack, + [{description, "An OTP library"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/src/eVPack.erl b/src/eVPack.erl new file mode 100644 index 0000000..ad9af05 --- /dev/null +++ b/src/eVPack.erl @@ -0,0 +1,1549 @@ +-module(eVPack). +-include("eVPack.hrl"). + +-export([ + encode/1 + , encode/3 + , decode/1 + , encodeAtom/1 + , encodeFloat/1 + , encodeInteger/1 + , encodeList/3 + , encodeMap/3 + , encodeString/1 + , encoder/3 + , buildIndexTable_1/2 + , buildIndexTable_2/2 + , buildIndexTable_4/2 + , buildIndexTable_8/2 +]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 编码 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec encode(term()) -> {ok, vpack()} | {error, any()}. +encode(Term) -> encode(Term, ?VpDefArrOpt, ?VpDefObjOpt). + +-spec encode(term(), vpOpt(), vpOpt()) -> {ok, vpack()} | {error, any()}. +encode(Term, ArrOpt, ObjOpt) -> + try {_v@1, __size@1} = encoder(Term, ArrOpt, ObjOpt), + {ok, _v@1} + catch + throw:Class -> {error, Class} + end. + +encoder(Map, ArrOpt, ObjOpt) when erlang:is_map(Map) -> + encodeMap(ObjOpt, Map, ArrOpt); +encoder(Atom, _, _) when erlang:is_atom(Atom) -> + encodeAtom(Atom); +encoder(Binary, _, _) when erlang:is_binary(Binary) -> + encodeString(Binary); +encoder(Integer, _, _) when erlang:is_integer(Integer) -> + encodeInteger(Integer); +encoder(Float, _, _) when erlang:is_float(Float) -> + encodeFloat(Float); +encoder(List, ArrOpt, ObjOpt) when erlang:is_list(List) -> + encodeList(ArrOpt, List, ObjOpt); +encoder(_Value, _, _) -> + {error, dataType(_Value)}. + + +dataType(Data) when is_list(Data) -> list; +dataType(Data) when is_integer(Data) -> integer; +dataType(Data) when is_binary(Data) -> binary; +dataType(Data) when is_function(Data) -> function; +dataType(Data) when is_tuple(Data) -> tuple; +dataType(Data) when is_atom(Data) -> atom; +dataType(Data) when is_bitstring(Data) -> bitstring; +dataType(Data) when is_boolean(Data) -> boolean; +dataType(Data) when is_float(Data) -> float; +dataType(Data) when is_number(Data) -> number; +dataType(Data) when is_pid(Data) -> pid; +dataType(Data) when is_port(Data) -> port; +dataType(_Data) -> not_know. + +encodeAtom(undefined) -> {<<24/integer>>, 1}; +encodeAtom(false) -> {<<25/integer>>, 1}; +encodeAtom(true) -> {<<26/integer>>, 1}; +encodeAtom(minKey) -> {<<30/integer>>, 1}; +encodeAtom(maxKey) -> {<<31/integer>>, 1}; +encodeAtom(illegal) -> {<<23/integer>>, 1}; +encodeAtom(null) -> {<<24/integer>>, 1}; +encodeAtom(Atom) -> + encodeString(erlang:atom_to_binary(Atom, utf8)). + +encodeInteger(0) -> + {<<48/integer>>, 1}; +encodeInteger(1) -> + {<<49/integer>>, 1}; +encodeInteger(2) -> + {<<50/integer>>, 1}; +encodeInteger(3) -> + {<<51/integer>>, 1}; +encodeInteger(4) -> + {<<52/integer>>, 1}; +encodeInteger(5) -> + {<<53/integer>>, 1}; +encodeInteger(6) -> + {<<54/integer>>, 1}; +encodeInteger(7) -> + {<<55/integer>>, 1}; +encodeInteger(8) -> + {<<56/integer>>, 1}; +encodeInteger(9) -> + {<<57/integer>>, 1}; +encodeInteger(-6) -> + {<<58/integer>>, 1}; +encodeInteger(-5) -> + {<<59/integer>>, 1}; +encodeInteger(-4) -> + {<<60/integer>>, 1}; +encodeInteger(-3) -> + {<<61/integer>>, 1}; +encodeInteger(-2) -> + {<<62/integer>>, 1}; +encodeInteger(-1) -> + {<<63/integer>>, 1}; +encodeInteger(Integer) -> + if + Integer < -9223372036854775808 -> + erlang:throw(<<"Cannot encode integers less than -9223372036854775808">>); + Integer < -36028797018963968 -> + {<<39/integer, Integer:64/integer-little-signed>>, 9}; + Integer < -140737488355328 -> + {<<38/integer, Integer:56/integer-little-signed>>, 8}; + Integer < -549755813888 -> + {<<37/integer, Integer:48/integer-little-signed>>, 7}; + Integer < -2147483648 -> + {<<36/integer, Integer:40/integer-little-signed>>, 6}; + Integer < -8388608 -> + {<<35/integer, Integer:32/integer-little-signed>>, 5}; + Integer < -32768 -> + {<<34/integer, Integer:24/integer-little-signed>>, 4}; + Integer < -128 -> + {<<33/integer, Integer:16/integer-little-signed>>, 3}; + Integer < 0 -> + {<<32/integer, Integer:8/integer-little-unsigned>>, 2}; + Integer < 256 -> + {<<40/integer, Integer:8/integer-little-unsigned>>, 2}; + Integer < 65536 -> + {<<41/integer, Integer:16/integer-little-unsigned>>, 3}; + Integer < 16777216 -> + {<<42/integer, Integer:24/integer-little-unsigned>>, 4}; + Integer < 4294967296 -> + {<<43/integer, Integer:32/integer-little-unsigned>>, 5}; + Integer < 1099511627776 -> + {<<44/integer, Integer:40/integer-little-unsigned>>, 6}; + Integer < 281474976710656 -> + {<<45/integer, Integer:48/integer-little-unsigned>>, 7}; + Integer < 72057594037927936 -> + {<<46/integer, Integer:56/integer-little-unsigned>>, 8}; + Integer < 18446744073709551616 -> + {<<47/integer, Integer:64/integer-little-unsigned>>, 9}; + true -> + erlang:throw(<<"Cannot encode integers greater than 18446744073709551616">>) + end. + +encodeFloat(Float) -> + {<<27/integer, Float:64/float-little>>, 9}. + +encodeString(BinStr) -> + StrSize = erlang:byte_size(BinStr), + if + StrSize =< 126 -> + {<<(StrSize + 64)/integer, BinStr/binary>>, StrSize + 1}; + StrSize < 256 -> + {<<192/integer, StrSize:8/integer-little-unsigned, BinStr/binary>>, StrSize + 2}; + StrSize < 65536 -> + {<<193/integer, StrSize:16/integer-little-unsigned, BinStr/binary>>, StrSize + 3}; + StrSize < 16777216 -> + {<<194/integer, StrSize:24/integer-little-unsigned, BinStr/binary>>, StrSize + 4}; + StrSize < 4294967296 -> + {<<195/integer, StrSize:32/integer-little-unsigned, BinStr/binary>>, StrSize + 5}; + StrSize < 1099511627776 -> + {<<196/integer, StrSize:40/integer-little-unsigned, BinStr/binary>>, StrSize + 6}; + StrSize < 281474976710656 -> + {<<197/integer, StrSize:48/integer-little-unsigned, BinStr/binary>>, StrSize + 7}; + StrSize < 72057594037927936 -> + {<<198/integer, StrSize:56/integer-little-unsigned, BinStr/binary>>, StrSize + 8}; + StrSize < 18446744073709551616 -> + {<<199/integer, StrSize:64/integer-little-unsigned, BinStr/binary>>, StrSize + 9}; + true -> + {<<191/integer, StrSize:64/integer-little-unsigned, BinStr/binary>>, StrSize + 9} + end. + +doEncodeMap(Iterator, ArrOpt, ObjOpt, AccList, SumSize) -> + case maps:next(Iterator) of + {Key, Value, NextIter} -> + {KeyEn, KeySize} = encodeString(asKey(Key)), + {ValueEn, ValueSize} = encoder(Value, ArrOpt, ObjOpt), + doEncodeMap(NextIter, ArrOpt, ObjOpt, [ValueEn, KeyEn | AccList], SumSize + KeySize + ValueSize); + none -> + {AccList, SumSize} + end. + +doEncodeMap(Iterator, ArrOpt, ObjOpt, AccList, Offsets, SumSize) -> + case maps:next(Iterator) of + {Key, Value, NextIter} -> + {KeyEn, KeySize} = encodeString(asKey(Key)), + {ValueEn, ValueSize} = encoder(Value, ArrOpt, ObjOpt), + doEncodeMap(NextIter, ArrOpt, ObjOpt, [ValueEn, KeyEn | AccList], [SumSize | Offsets], SumSize + KeySize + ValueSize); + none -> + {AccList, Offsets, SumSize} + end. + +doEncodeMap([], _Map, _ArrOpt, _ObjOpt, AccList, Offsets, SumSize) -> + {AccList, Offsets, SumSize}; +doEncodeMap([OneKeys | Left], Map, ArrOpt, ObjOpt, AccList, Offsets, SumSize) -> + case Map of + #{OneKeys := Value} -> + {KeyEn, KeySize} = encodeString(OneKeys), + {ValueEn, ValueSize} = encoder(Value, ArrOpt, ObjOpt), + doEncodeMap(Left, Map, ArrOpt, ObjOpt, [ValueEn, KeyEn | AccList], [SumSize | Offsets], SumSize + KeySize + ValueSize); + _ -> + AtomKey = binary_to_atom(OneKeys, utf8), + case Map of + #{AtomKey := Value} -> + {KeyEn, KeySize} = encodeString(OneKeys), + {ValueEn, ValueSize} = encoder(Value, ArrOpt, ObjOpt), + doEncodeMap(Left, Map, ArrOpt, ObjOpt, [ValueEn, KeyEn | AccList], [SumSize | Offsets], SumSize + KeySize + ValueSize); + _ -> + erlang:throw(<<"doEncodeMap not found the value ", OneKeys/binary>>) + end + end. + +encodeMap(?vpObjNcNs, Map, ArrOpt) -> + MapSize = erlang:map_size(Map), + case MapSize == 0 of + true -> + {<<10/integer>>, 1}; + _ -> + {AccList, Offsets, SumSize} = doEncodeMap(maps:iterator(Map), ArrOpt, ?vpObjNcNs, [], [], 0), + IoData = lists:reverse(AccList), + case MapSize >= 1000 of + false -> + encodeUnSortMapIndexTable(IoData, MapSize, Offsets, SumSize); + _ -> + encodeUnSortMapIndexTable(erlang:iolist_to_binary(IoData), MapSize, Offsets, SumSize) + end + end; +encodeMap(?vpObjYc, Map, ArrOpt) -> + MapSize = erlang:map_size(Map), + case MapSize == 0 of + true -> + {<<10/integer>>, 1}; + _ -> + {AccList, SumSize} = doEncodeMap(maps:iterator(Map), ArrOpt, ?vpObjYc, [], 0), + IoData = lists:reverse(AccList), + case MapSize >= 1000 of + false -> + encodeCompactData(<<20/integer>>, IoData, SumSize, MapSize); + _ -> + encodeCompactData(<<20/integer>>, erlang:iolist_to_binary(IoData), SumSize, MapSize) + end + end; +encodeMap(?vpObjNcYs, Map, ArrOpt) -> + MapSize = erlang:map_size(Map), + case MapSize == 0 of + true -> + {<<10/integer>>, 1}; + _ -> + Keys = maps:keys(Map), + StrKeys = [asKey(OneKey) || OneKey <- Keys], + {AccList, Offsets, SumSize} = doEncodeMap(lists:sort(StrKeys), Map, ArrOpt, ?vpObjNcYs, [], [], 0), + IoData = lists:reverse(AccList), + case MapSize >= 1000 of + false -> + encodeSortMapIndexTable(IoData, MapSize, Offsets, SumSize); + _ -> + encodeSortMapIndexTable(erlang:iolist_to_binary(IoData), MapSize, Offsets, SumSize) + end + end. + +encodeSortMapIndexTable(IoData, Count, Offsets, SumSize) -> + TemSize = SumSize + Count, + if + TemSize < 253 -> + AllSize = TemSize + 3, + Header = <<11/integer, AllSize:8/integer-unsigned, Count:8/integer-unsigned>>, + {[Header, IoData, buildIndexTable_1(Offsets, 3)], AllSize}; + TemSize + Count < 65531 -> + AllSize = TemSize + Count + 5, + Header = <<12/integer, AllSize:16/integer-little-unsigned, Count:16/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_2(Offsets, 5)], AllSize}; + TemSize + Count * 3 < 4294967287 -> + AllSize = TemSize + Count * 3 + 9, + Header = <<13/integer, AllSize:32/integer-little-unsigned, Count:32/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_4(Offsets, 9)], AllSize}; + TemSize + Count * 7 < 18446744073709551599 -> + AllSize = TemSize + Count * 7 + 17, + Header = <<14/integer, AllSize:64/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_8(Offsets, 17), <>], AllSize}; + true -> + erlang:throw(<<"too much size">>) + end. + +encodeUnSortMapIndexTable(IoData, Count, Offsets, SumSize) -> + TemSize = SumSize + Count, + if + TemSize < 253 -> + AllSize = TemSize + 3, + Header = <<15/integer, AllSize:8/integer-unsigned, Count:8/integer-unsigned>>, + {[Header, IoData, buildIndexTable_1(Offsets, 3)], AllSize}; + TemSize + Count < 65531 -> + AllSize = TemSize + Count + 5, + Header = <<16/integer, AllSize:16/integer-little-unsigned, Count:16/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_2(Offsets, 5)], AllSize}; + TemSize + Count * 3 < 4294967287 -> + AllSize = TemSize + Count * 3 + 9, + Header = <<17/integer, AllSize:32/integer-little-unsigned, Count:32/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_4(Offsets, 9)], AllSize}; + TemSize + Count * 7 < 18446744073709551599 -> + AllSize = TemSize + Count * 7 + 17, + Header = <<18/integer, AllSize:64/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_8(Offsets, 17), <>], AllSize}; + true -> + erlang:throw(<<"encode map too much size">>) + end. + +buildIndexTable_1(Offsets, StartSize) -> + <<<<(OneOff + StartSize):1/integer-little-unsigned-unit:8>> || OneOff <- lists:reverse(Offsets)>>. + +buildIndexTable_2(Offsets, StartSize) -> + <<<<(OneOff + StartSize):2/integer-little-unsigned-unit:8>> || OneOff <- lists:reverse(Offsets)>>. + +buildIndexTable_4(Offsets, StartSize) -> + <<<<(OneOff + StartSize):4/integer-little-unsigned-unit:8>> || OneOff <- lists:reverse(Offsets)>>. + +buildIndexTable_8(Offsets, StartSize) -> + <<<<(OneOff + StartSize):8/integer-little-unsigned-unit:8>> || OneOff <- lists:reverse(Offsets)>>. + +compactIntegerList(Integer, AccList) -> + case Integer < 128 of + true -> + [<> | AccList]; + _ -> + TemInteger = Integer band 127 bor 128, + NewInteger = Integer bsr 7, + compactIntegerList(NewInteger, [<> | AccList]) + end. + +compactInteger(Value, Reverse) -> + CompactList = compactIntegerList(Value, []), + case Reverse of + false -> lists:reverse(CompactList); + _ -> CompactList + end. + +compactSize(AllSize) -> + TemByte = erlang:ceil(AllSize / 128), + FinalSize = AllSize + TemByte, + LastSize = + case TemByte == erlang:ceil(FinalSize / 128) of + false -> FinalSize + 1; + true -> FinalSize + end, + {compactInteger(LastSize, false), LastSize}. + +encodeCompactData(Type, IoData, SumSize, Count) -> + CompactList = compactInteger(Count, true), + AllSize = SumSize + 1 + lists:length(CompactList), + {TotalSize, FinalSize} = compactSize(AllSize), + {[Type, TotalSize, IoData | CompactList], FinalSize}. + + +asKey(Value) when erlang:is_atom(Value) -> erlang:atom_to_binary(Value, utf8); +asKey(Value) when erlang:is_binary(Value) -> Value; +asKey(_Value) -> erlang:error(<<"Invalid key">>). + +doEncodeList([], _ArrOpt, _ObjOpt, AccList, SumSize, Count) -> + {AccList, SumSize, Count}; +doEncodeList([One | Left], ArrOpt, ObjOpt, AccList, SumSize, Count) -> + {ValueEn, ValueSize} = encoder(One, ArrOpt, ObjOpt), + doEncodeList(Left, ArrOpt, ObjOpt, [ValueEn | AccList], SumSize + ValueSize, Count + 1). + +doEncodeList([], _ArrOpt, _ObjOpt, AccList, Offsets, SumSize, Count, SizeOrIsNot) -> + {AccList, Offsets, SumSize, Count, SizeOrIsNot}; +doEncodeList([One | Left], ArrOpt, ObjOpt, AccList, Offsets, SumSize, Count, SizeOrIsNot) -> + {ValueEn, ValueSize} = encoder(One, ArrOpt, ObjOpt), + case SizeOrIsNot of + true -> + doEncodeList(Left, ArrOpt, ObjOpt, [ValueEn | AccList], [SumSize | Offsets], ValueSize + SumSize, Count + 1, SizeOrIsNot); + init -> + doEncodeList(Left, ArrOpt, ObjOpt, [ValueEn | AccList], [SumSize | Offsets], ValueSize + SumSize, Count + 1, ValueSize); + _ -> + doEncodeList(Left, ArrOpt, ObjOpt, [ValueEn | AccList], [SumSize | Offsets], ValueSize + SumSize, Count + 1, ValueSize =/= SizeOrIsNot orelse SizeOrIsNot) + end. + +encodeList(?vpArrNc, List, ObjOpt) -> + case List of + [] -> + {<<1/integer>>, 1}; + _ -> + {AccList, Offsets, SumSize, Count, IsNotSameSize} = doEncodeList(List, ?vpArrNc, ObjOpt, [], [], 0, 0, init), + + IoData = lists:reverse(AccList), + case Count >= 1000 of + false -> + case IsNotSameSize of + true -> + encodeListWithoutIndexTable(IoData, SumSize); + _ -> + encodeListWithIndexTable(IoData, Count, Offsets, SumSize) + end; + _ -> + case IsNotSameSize of + true -> + encodeListWithoutIndexTable(erlang:iolist_to_binary(IoData), SumSize); + _ -> + encodeListWithIndexTable(erlang:iolist_to_binary(IoData), Count, Offsets, SumSize) + end + end + end; +encodeList(?vpArrYc, List, ObjOpt) -> + case List of + [] -> + {<<1/integer>>, 1}; + _ -> + {AccList, SumSize, Count} = doEncodeList(List, ?vpArrYc, ObjOpt, [], 0, 0), + + IoData = lists:reverse(AccList), + case Count >= 1000 of + false -> + encodeCompactData(<<19/integer>>, IoData, SumSize, Count); + _ -> + encodeCompactData(<<19/integer>>, erlang:iolist_to_binary(IoData), SumSize, Count) + end + end. + +encodeListWithoutIndexTable(IoData, SumSize) -> + if + SumSize < 254 -> + AllSize = SumSize + 2, + Header = <<2/integer, AllSize:1/integer-little-unsigned-unit:8>>, + {[Header, IoData], AllSize}; + SumSize < 65533 -> + AllSize = SumSize + 3, + Header = <<3/integer, AllSize:2/integer-little-unsigned-unit:8>>, + {[Header, IoData], AllSize}; + SumSize < 4294967291 -> + AllSize = SumSize + 5, + Header = <<4/integer, AllSize:4/integer-little-unsigned-unit:8>>, + {[Header, IoData], AllSize}; + SumSize < 18446744073709551607 -> + AllSize = SumSize + 9, + Header = <<5/integer, AllSize:8/integer-little-unsigned-unit:8>>, + {[Header, IoData], AllSize}; + true -> + erlang:throw(<<"encode map too much size">>) + end. + +encodeListWithIndexTable(IoData, Count, Offsets, SumSize) -> + TemSize = SumSize + Count, + if + TemSize < 253 -> + AllSize = TemSize + 3, + Header = <<6/integer, AllSize:8/integer-unsigned, Count:8/integer-unsigned>>, + {[Header, IoData, buildIndexTable_1(Offsets, 3)], AllSize}; + TemSize + Count < 65531 -> + AllSize = TemSize + Count + 5, + Header = <<7/integer, AllSize:16/integer-little-unsigned, Count:16/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_2(Offsets, 5)], AllSize}; + TemSize + Count * 3 < 4294967287 -> + AllSize = TemSize + Count * 3 + 9, + Header = <<8/integer, AllSize:32/integer-little-unsigned, Count:32/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_4(Offsets, 9)], AllSize}; + TemSize + Count * 7 < 18446744073709551599 -> + AllSize = TemSize + Count * 7 + 17, + Header = <<9/integer, AllSize:64/integer-little-unsigned>>, + {[Header, IoData, buildIndexTable_8(Offsets, 17), <>], AllSize}; + true -> + erlang:throw(<<"encode map too much size">>) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% decode %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +decode(DataBin) -> + try decoder(DataBin) of + {Value, <<>>} -> {ok, Value}; + {Value, Tail} -> {ok, {Value, Tail}} + catch + Class:Err:Strace -> + {error, Class, Err, Strace} + end. + +decoder(DataBin) -> + case DataBin of + <> -> + decoder(Type, RestBin); + _ -> + erlang:throw(unexpected_end) + end. + +decoder(0, RestBin) -> + erlang:throw({unsupported_type, RestBin}); +decoder(1, RestBin) -> + {[], RestBin}; +decoder(2, RestBin) -> + parseArrayWithoutIndexTable(2, RestBin); +decoder(3, RestBin) -> + parseArrayWithoutIndexTable(3, RestBin); +decoder(4, RestBin) -> + parseArrayWithoutIndexTable(4, RestBin); +decoder(5, RestBin) -> + parseArrayWithoutIndexTable(5, RestBin); +decoder(6, RestBin) -> + parseArrayWithIndexTable(6, RestBin); +decoder(7, RestBin) -> + parseArrayWithIndexTable(7, RestBin); +decoder(8, RestBin) -> + parseArrayWithIndexTable(8, RestBin); +decoder(9, RestBin) -> + parseArrayWithIndexTable(9, RestBin); +decoder(10, RestBin) -> + {#{}, RestBin}; +decoder(11, RestBin) -> + parseObject(11, RestBin); +decoder(12, RestBin) -> + parseObject(12, RestBin); +decoder(13, RestBin) -> + parseObject(13, RestBin); +decoder(14, RestBin) -> + parseObject(14, RestBin); +decoder(15, RestBin) -> + parseObject(15, RestBin); +decoder(16, RestBin) -> + parseObject(16, RestBin); +decoder(17, RestBin) -> + parseObject(17, RestBin); +decoder(18, RestBin) -> + parseObject(18, RestBin); +decoder(19, RestBin) -> + parseCompactArray(RestBin); +decoder(20, RestBin) -> + parseCompactObject(RestBin); +decoder(21, _RestBin) -> + erlang:throw({unsupported_type, 21}); +decoder(22, _RestBin) -> + erlang:throw({unsupported_type, 22}); +decoder(23, RestBin) -> + {illegal, RestBin}; +decoder(24, RestBin) -> {undefined, RestBin}; +decoder(25, RestBin) -> {false, RestBin}; +decoder(26, RestBin) -> {true, RestBin}; +decoder(27, RestBin) -> + <> = RestBin, + {Float, LeftBin}; +decoder(28, RestBin) -> + <> = RestBin, + {TimeMs, LeftBin}; +decoder(29, _RestBin) -> + erlang:throw({unsupported_type, 29}); +decoder(30, RestBin) -> + {min_key, RestBin}; +decoder(31, RestBin) -> + {max_key, RestBin}; +decoder(32, RestBin) -> + parseInt(1, RestBin); +decoder(33, RestBin) -> + parseInt(2, RestBin); +decoder(34, RestBin) -> + parseInt(3, RestBin); +decoder(35, RestBin) -> + parseInt(4, RestBin); +decoder(36, RestBin) -> + parseInt(5, RestBin); +decoder(37, RestBin) -> + parseInt(6, RestBin); +decoder(38, RestBin) -> + parseInt(7, RestBin); +decoder(39, RestBin) -> + parseInt(8, RestBin); +decoder(40, RestBin) -> + parseUint(1, RestBin); +decoder(41, RestBin) -> + parseUint(2, RestBin); +decoder(42, RestBin) -> + parseUint(3, RestBin); +decoder(43, RestBin) -> + parseUint(4, RestBin); +decoder(44, RestBin) -> + parseUint(5, RestBin); +decoder(45, RestBin) -> + parseUint(6, RestBin); +decoder(46, RestBin) -> + parseUint(7, RestBin); +decoder(47, RestBin) -> + parseUint(8, RestBin); +decoder(48, RestBin) -> + {0, RestBin}; +decoder(49, RestBin) -> + {1, RestBin}; +decoder(50, RestBin) -> + {2, RestBin}; +decoder(51, RestBin) -> + {3, RestBin}; +decoder(52, RestBin) -> + {4, RestBin}; +decoder(53, RestBin) -> + {5, RestBin}; +decoder(54, RestBin) -> + {6, RestBin}; +decoder(55, RestBin) -> + {7, RestBin}; +decoder(56, RestBin) -> + {8, RestBin}; +decoder(57, RestBin) -> + {9, RestBin}; +decoder(58, RestBin) -> + {-6, RestBin}; +decoder(59, RestBin) -> + {-5, RestBin}; +decoder(60, RestBin) -> + {-4, RestBin}; +decoder(61, RestBin) -> + {-3, RestBin}; +decoder(62, RestBin) -> + {-2, RestBin}; +decoder(63, RestBin) -> + {-1, RestBin}; +decoder(64, RestBin) -> + {<<>>, RestBin}; +decoder(65, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(66, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(67, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(68, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(69, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(70, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(71, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(72, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(73, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(74, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(75, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(76, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(77, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(78, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(79, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(80, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(81, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(82, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(83, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(84, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(85, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(86, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(87, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(88, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(89, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(90, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(91, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(92, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(93, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(94, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(95, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(96, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(97, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(98, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(99, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(100, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(101, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(102, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(103, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(104, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(105, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(106, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(107, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(108, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(109, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(110, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(111, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(112, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(113, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(114, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(115, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(116, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(117, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(118, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(119, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(120, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(121, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(122, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(123, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(124, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(125, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(126, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(127, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(128, RestBin) -> + <> = RestBin, + {BinStr, LeftBin}; +decoder(129, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 65 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(130, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 66 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(131, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 67 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(132, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 68 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(133, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 69 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(134, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 70 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(135, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 71 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(136, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 72 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(137, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 73 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(138, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 74 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(139, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 75 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(140, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 76 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(141, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 77 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(142, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 78 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(143, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 79 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(144, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 80 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(145, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 81 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(146, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 82 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(147, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 83 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(148, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 84 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(149, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 85 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(150, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 86 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(151, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 87 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(152, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 88 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(153, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 89 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(154, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 90 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(155, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 91 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(156, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 92 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(157, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 93 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(158, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 94 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(159, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 95 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(160, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 96 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(161, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 97 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(162, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 98 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(163, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 99 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(164, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 100 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(165, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 101 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(166, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 102 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(167, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 103 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(168, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 104 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(169, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 105 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(170, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 106 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(171, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 107 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(172, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 108 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(173, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 109 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(174, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 110 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(175, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 111 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(176, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 112 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(177, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 113 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(178, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 114 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(179, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 115 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(180, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 116 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(181, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 117 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(182, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 118 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(183, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 119 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(184, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 120 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(185, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 121 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(186, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 122 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(187, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 123 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(188, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 124 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(189, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 125 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(190, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / 126 > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(191, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(192, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(193, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(194, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(195, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(196, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(197, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(198, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(199, RestBin) -> + <> = RestBin, + RefSize = binary:referenced_byte_size(RestBin), + case RefSize / Length > ?VpBinaryCopyRatio of + true -> + {binary:copy(BinStr), LeftBin}; + _ -> + {BinStr, LeftBin} + end; +decoder(_, _) -> + erlang:throw(unexpected_end). + + +parseArrayElements(<<>>, AccList) -> lists:reverse(AccList); +parseArrayElements(DataBin, AccList) -> + {Elem, Rest} = decoder(DataBin), + parseArrayElements(Rest, [Elem | AccList]). + +parseArrayWithIndexTable(9, <>) -> + DataSize = SumSize - 1 - 8 - 8, + <> = RestBin, + IndexSize = Length * 8, + DataSize = DataSize - IndexSize, + <> = DataBin, + ArrList = parseArrayElements(ArrData, []), + {ArrList, LeftBin}; +parseArrayWithIndexTable(Type, DataBin) -> + SizeBytes = 1 bsl (Type - 6), + <> = DataBin, + IndexSize = SizeBytes * Length, + DataSize = erlang:byte_size(RestBin), + DataLeftBin = skipZeros(RestBin), + ZerosSize = DataSize - erlang:byte_size(DataLeftBin), + ArrSize = SumSize - 1 - 2 * SizeBytes - ZerosSize - IndexSize, + <> = DataLeftBin, + ArrList = parseArrayElements(ArrData, []), + {ArrList, LeftBin}. + +parseArrayWithoutIndexTable(Type, DataBin) -> + SizeBytes = 1 bsl (Type - 2), + <> = DataBin, + DataSize = erlang:byte_size(RestBin), + DataLeftBin = skipZeros(RestBin), + ZerosSize = DataSize - erlang:byte_size(DataLeftBin), + ArrSize = SumSize - SizeBytes - 1 - ZerosSize, + <> = DataLeftBin, + ArrList = parseArrayElements(ArrData, []), + {ArrList, LeftBin}. + +parseCompactArray(DataBin) -> + {ArrData, _Length, RestBin} = parseCompactHeader(DataBin), + ArrList = parseArrayElements(ArrData, []), + {ArrList, RestBin}. + +parseCompactHeader(DataBin) -> + {Size, RestBin} = parseLength(DataBin, 0, 0, false), + DataSize = Size - (erlang:byte_size(DataBin) - erlang:byte_size(RestBin)) - 1, + <> = RestBin, + {Length, HeaderBin} = parseLength(ArrData, 0, 0, true), + {HeaderBin, Length, LeftBin}. + +parseCompactObject(DataBin) -> + {ObjData, Length, RestBin} = parseCompactHeader(DataBin), + {Obj, <<>>} = parseObjectMembers(Length, #{}, ObjData), + {Obj, RestBin}. + +parseInt(IntegerSize, DataBin) -> + <> = DataBin, + {Integer, LeftBin}. + +parseUint(IntegerSize, DataBin) -> + <> = DataBin, + {Integer, LeftBin}. + +parseLength(DataBin, Len, Pt, Reverse) -> + {LastV, LeftBin} = + case Reverse of + false -> + <> = DataBin, + {V, RestBin}; + _ -> + Size = erlang:byte_size(DataBin) - 1, + <> = DataBin, + {V, RestBin} + end, + NewLen = Len + (LastV band 127 bsl Pt), + NewPt = Pt + 7, + case LastV band 128 /= 0 of + false -> {NewLen, LeftBin}; + true -> parseLength(LeftBin, NewLen, NewPt, Reverse) + end. + +parseObject(Type, DataBin) -> + SizeBytes = 1 bsl (Type - 11), + <> = DataBin, + DataSize = SumSize - 1 - 2 * SizeBytes, + <> = ObjBin, + IndexTableSize = Length * SizeBytes, + {Obj, <<_IndexTable:IndexTableSize/binary>>} = parseObjectMembers(Length, #{}, skipZeros(ZerosBin)), + {Obj, LeftBin}. + +parseObjectMembers(0, Obj, DataBin) -> + {Obj, DataBin}; +parseObjectMembers(Length, Obj, DataBin) -> + {Key, ValueDataBin} = decoder(DataBin), + {Value, LeftBin} = decoder(ValueDataBin), + NewObj = Obj#{Key => Value}, + parseObjectMembers(Length - 1, NewObj, LeftBin). + +skipZeros(<<0/integer, LeftBin/binary>>) -> + skipZeros(LeftBin); +skipZeros(DataBin) -> DataBin. + + + diff --git a/src/encoderTest.erl b/src/encoderTest.erl new file mode 100644 index 0000000..da62179 --- /dev/null +++ b/src/encoderTest.erl @@ -0,0 +1,251 @@ +-module(encoderTest). +-include("erlVPack.hrl"). + +-compile([no_auto_import]). + +-export([ + test/0 +]). + +test() -> + try do() of + _ -> + ok + catch + E:C:S -> + {E:C:S} + end. + +do() -> + <<23>> = erlVPack:encode(illegal), + <<24>> = erlVPack:encode(nil), + <<25>> = erlVPack:encode(false), + <<26>> = erlVPack:encode(true), + <<27, 102, 102, 102, 102, 102, 182, 96, 64>> = erlVPack:encode(1.33699999999999988631e+2), + <<27, 102, 102, 102, 102, 102, 182, 96, 192>> = erlVPack:encode(-1.33699999999999988631e+2), + <<30>> = erlVPack:encode(min_key), + <<31>> = erlVPack:encode(max_key), + <<48>> = erlVPack:encode(0), + <<49>> = erlVPack:encode(1), + <<50>> = erlVPack:encode(2), + <<51>> = erlVPack:encode(3), + <<52>> = erlVPack:encode(4), + <<53>> = erlVPack:encode(5), + <<54>> = erlVPack:encode(6), + <<55>> = erlVPack:encode(7), + <<56>> = erlVPack:encode(8), + <<57>> = erlVPack:encode(9), + <<58>> = erlVPack:encode(-6), + <<59>> = erlVPack:encode(-5), + <<60>> = erlVPack:encode(-4), + <<61>> = erlVPack:encode(-3), + <<62>> = erlVPack:encode(-2), + <<63>> = erlVPack:encode(-1), + <<32, (-7)/integer-little-signed>> = erlVPack:encode(-7), + <<32, 128>> = erlVPack:encode(-128), + <<33, 0, 128>> = erlVPack:encode(-32768), + <<34, 0, 0, 128>> = erlVPack:encode(-8388608), + <<35, 0, 0, 0, 128>> = erlVPack:encode(-2147483648), + <<36, 0, 0, 0, 0, 128>> = erlVPack:encode(-549755813888), + <<37, 0, 0, 0, 0, 0, 128>> = erlVPack:encode(-140737488355328), + <<38, 0, 0, 0, 0, 0, 0, 128>> = erlVPack:encode(-36028797018963968), + <<39, 0, 0, 0, 0, 0, 0, 0, 128>> = erlVPack:encode(-9223372036854775808), + <<40, 255>> = erlVPack:encode(255), + <<41, 255, 255>> = erlVPack:encode(65535), + <<42, 255, 255, 255>> = erlVPack:encode(16777215), + <<43, 255, 255, 255, 255>> = erlVPack:encode(4294967295), + <<44, 255, 255, 255, 255, 255>> = erlVPack:encode(1099511627775), + <<45, 255, 255, 255, 255, 255, 255>> = erlVPack:encode(281474976710655), + <<46, 255, 255, 255, 255, 255, 255, 255>> = erlVPack:encode(72057594037927935), + <<47, 255, 255, 255, 255, 255, 255, 255, 255>> = erlVPack:encode(18446744073709551615), + <<75, 72, 97, 108, 108, 111, 32, 87, 101, 108, 116, 33>> = erlVPack:encode(<<"Hallo Welt!">>), + <<76, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33>> = erlVPack:encode(<<"Hello World!">>), + Str = <<"Lorem ipsum dolor sit amet, consectetuer " + "adipiscing elit. Aenean commodo ligula " + "eget dolor. ", + "Aenean massa. Cum sociis natoque penatibus " + "et magnis dis parturient montes, nascetur " + "ridiculus mus. ", + "Donec quam felis, ultricies nec, pellentesque " + "eu, pretium quis, sem. Nulla consequat " + "massa quis enim. ", + "Donec pede justo, fringilla vel, aliquet " + "nec, vulputate eget, arcu. In enim justo, " + "rhoncus ut, imperdiet a, ", + "venenatis vitae, justo. Nullam dictum " + "felis eu pede mollis pretium. Integer " + "tincidunt. Cras dapibus. ", + "Vivamus elementum semper nisi. Aenean " + "vulputate eleifend tellus.">>, + StrBin = <<191, 55, 2, 0, 0, 0, 0, 0, 0, 76, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109, 32, 100, 111, 108, 111, + 114, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, + 114, 32, 97, 100, 105, 112, 105, 115, 99, 105, 110, 103, 32, 101, 108, 105, 116, 46, 32, 65, 101, 110, 101, + 97, 110, 32, 99, 111, 109, 109, 111, 100, 111, 32, 108, 105, 103, 117, 108, 97, 32, 101, 103, 101, + 116, 32, 100, 111, 108, 111, 114, 46, 32, 65, 101, 110, 101, 97, 110, 32, 109, 97, 115, 115, + 97, 46, 32, 67, 117, 109, 32, 115, 111, 99, 105, 105, 115, 32, 110, 97, 116, 111, 113, 117, + 101, 32, 112, 101, 110, 97, 116, 105, 98, 117, 115, 32, 101, 116, 32, 109, 97, 103, 110, 105, + 115, 32, 100, 105, 115, 32, 112, 97, 114, 116, 117, 114, 105, 101, 110, 116, 32, 109, 111, 110, + 116, 101, 115, 44, 32, 110, 97, 115, 99, 101, 116, 117, 114, 32, 114, 105, 100, 105, 99, 117, + 108, 117, 115, 32, 109, 117, 115, 46, 32, 68, 111, 110, 101, 99, 32, 113, 117, 97, 109, 32, + 102, 101, 108, 105, 115, 44, 32, 117, 108, 116, 114, 105, 99, 105, 101, 115, 32, 110, 101, 99, + 44, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 101, 117, 44, 32, 112, + 114, 101, 116, 105, 117, 109, 32, 113, 117, 105, 115, 44, 32, 115, 101, 109, 46, 32, 78, 117, + 108, 108, 97, 32, 99, 111, 110, 115, 101, 113, 117, 97, 116, 32, 109, 97, 115, 115, 97, 32, + 113, 117, 105, 115, 32, 101, 110, 105, 109, 46, 32, 68, 111, 110, 101, 99, 32, 112, 101, 100, + 101, 32, 106, 117, 115, 116, 111, 44, 32, 102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 118, + 101, 108, 44, 32, 97, 108, 105, 113, 117, 101, 116, 32, 110, 101, 99, 44, 32, 118, 117, 108, + 112, 117, 116, 97, 116, 101, 32, 101, 103, 101, 116, 44, 32, 97, 114, 99, 117, 46, 32, 73, + 110, 32, 101, 110, 105, 109, 32, 106, 117, 115, 116, 111, 44, 32, 114, 104, 111, 110, 99, 117, + 115, 32, 117, 116, 44, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 32, 97, 44, 32, 118, + 101, 110, 101, 110, 97, 116, 105, 115, 32, 118, 105, 116, 97, 101, 44, 32, 106, 117, 115, 116, + 111, 46, 32, 78, 117, 108, 108, 97, 109, 32, 100, 105, 99, 116, 117, 109, 32, 102, 101, 108, + 105, 115, 32, 101, 117, 32, 112, 101, 100, 101, 32, 109, 111, 108, 108, 105, 115, 32, 112, 114, + 101, 116, 105, 117, 109, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 116, 105, 110, 99, 105, + 100, 117, 110, 116, 46, 32, 67, 114, 97, 115, 32, 100, 97, 112, 105, 98, 117, 115, 46, 32, + 86, 105, 118, 97, 109, 117, 115, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 32, 115, 101, + 109, 112, 101, 114, 32, 110, 105, 115, 105, 46, 32, 65, 101, 110, 101, 97, 110, 32, 118, 117, + 108, 112, 117, 116, 97, 116, 101, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, 116, 101, 108, + 108, 117, 115, 46>>, + StrBin = erlVPack:encode(Str), + + <<1>>, erlVPack:encode([]), + <<2, 5, "1", "2", "3">>, erlVPack:encode([1, 2, 3]), + + <<19, 6, 49, 40, 16, 2>> = erlVPack:encode([1, 16], [{compact_arrays, true}]), + Arr = <<"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. ", "Aenean massa. Cum sociis ...">>, + ArrList = [Arr, 1, 2, 42], + <<19, _/binary>> = ArrBin = erlVPack:encode(ArrList, ?vpArrYc, ?VpDefObjOpt), + ArrList = erlVPack:decode(ArrBin), + ArrLists = <<2, 12, 2, 5, 49, 50, 51, 2, 5, 49, 50, 51>>, + ArrsBin = [[1, 2, 3], [1, 2, 3]], + ArrLists = erlVPack:encode(ArrsBin), + <<10>> = erlVPack:encode(#{}), + <<11, 8, 1, 65, 97, 65, 98, 3>> = erlVPack:encode(#{<<"a">> => <<"b">>}), + <<11, 13, 2, 65, 97, 65, 98, 65, 98, 65, 97, 3, 7>> = erlVPack:encode(#{a => <<"b">>, b => <<"a">>}), + Maps = #{<<"0">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"1">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"2">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"3">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}, + <<"4">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>}}, + MapBin = <<11, 233, 5, 65, 48, 11, 43, 5, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, + 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 65, 49, 11, + 43, 5, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, + 115, 116, 65, 52, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 65, 50, 11, 43, 5, 65, 48, 68, 116, 101, 115, 116, 65, 49, + 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 3, 10, + 17, 24, 31, 65, 51, 11, 43, 5, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, + 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 65, 52, 11, 43, + 5, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, + 116, 65, 52, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 3, 48, 93, 138, 183>>, + MapBin = erlVPack:encode(Maps), + Mapss = #{<<"0">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"1">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"2">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"3">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"4">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"5">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"6">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"7">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"8">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}, + <<"9">> => + #{<<"0">> => <<"test">>, <<"1">> => <<"test">>, + <<"2">> => <<"test">>, <<"3">> => <<"test">>, + <<"4">> => <<"test">>, <<"5">> => <<"test">>, + <<"6">> => <<"test">>, <<"7">> => <<"test">>, + <<"8">> => <<"test">>, <<"9">> => <<"test">>}}, + MapsBin = <<12, 107, 3, 10, 0, 65, 48, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, + 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, 116, 101, 115, 116, + 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 38, 45, + 52, 59, 66, 65, 49, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, + 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, + 68, 116, 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, + 115, 116, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 65, 50, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, + 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, + 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, 116, 101, 115, 116, 65, 55, 68, 116, 101, 115, + 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 38, 45, 52, 59, + 66, 65, 51, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, + 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, 116, + 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, + 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 65, 52, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, + 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, + 68, 116, 101, 115, 116, 65, 54, 68, 116, 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, + 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 65, 53, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, + 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, + 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, 116, 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, + 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, + 65, 54, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, + 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, 116, 101, 115, 116, 65, 55, + 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, 3, 10, 17, 24, 31, + 38, 45, 52, 59, 66, 65, 55, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, 116, + 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, 116, + 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, 3, 10, 17, 24, + 31, 38, 45, 52, 59, 66, 65, 56, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, 68, + 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, 68, + 116, 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, 115, 116, 3, 10, 17, + 24, 31, 38, 45, 52, 59, 66, 65, 57, 11, 83, 10, 65, 48, 68, 116, 101, 115, 116, 65, 49, 68, 116, 101, 115, 116, 65, 50, + 68, 116, 101, 115, 116, 65, 51, 68, 116, 101, 115, 116, 65, 52, 68, 116, 101, 115, 116, 65, 53, 68, 116, 101, 115, 116, 65, 54, + 68, 116, 101, 115, 116, 65, 55, 68, 116, 101, 115, 116, 65, 56, 68, 116, 101, 115, 116, 65, 57, 68, 116, 101, + 115, 116, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 5, 0, 90, 0, 175, 0, 4, 1, 89, 1, 174, 1, 3, 2, 88, 2, 173, 2, 2, 3>>, + MapsBin = erlVPack:encode(Mapss), + MapCom = #{<<"a">> => 12, <<"b">> => true, <<"c">> => <<"xyz">>}, + MapComBin = <<20, 16, 65, 97, 40, 12, 65, 98, 26, 65, 99, 67, 120, 121, 122, 3>>, + MapComBin = erlVPack:encode(MapCom, ?VpDefArrOpt, ?vpObjYc).