erlAarango 二进制序列化库
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 

23 KiB

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中,“有效负载”是指不包括长度说明的实际数据。