PackStream

PackStream 是一种用于交换丰富类型数据的二进制表示格式。它为 Bolt 消息协议提供了一个语法层。

版本 1

PackStream 是一种通用数据序列化格式,最初受(但与)MessagePack启发。

该格式提供了一个与 Cypher 支持的类型完全兼容的类型系统,有关更多信息,请参阅Cypher 手册→值和类型

PackStream 提供了许多核心数据类型,其中许多都支持多种二进制表示形式,以及灵活的扩展机制。

下表描述了核心数据类型。

表 1. 核心数据类型
数据类型 描述

空值

缺少或空值

布尔值

整数

带符号的 64 位整数

浮点数

64 位浮点数

字节数组

字节数组

字符串

Unicode 文本,UTF-8

列表

值的排序集合

字典

键值对的集合(不保证顺序)

结构

具有类型签名的复合值

不包括无符号整数或 32 位浮点数。这是为了允许在客户端语言之间具有更广泛的兼容性而做出的刻意设计决策。

一般表示

每个序列化后的 PackStream 值都以一个标记字节开头。

标记包含数据类型信息以及需要该信息类型的直接或间接大小信息。大小信息是如何编码的因标记类型而异。

某些值,例如布尔值 true,可以在单个标记字节中编码。许多小整数(具体来说,介于 -16 和 +127 之间(包括 -16 和 +127))也以单个字节编码。

一些标记字节保留用于格式本身的未来扩展。这些字节不应使用,并且在传入流中遇到它们应被视为错误。

大小可变的值

某些表示形式是可变长度的,并且其大小在表示形式中显式编码。此类值通常以单个标记字节开头,后跟大小,然后是数据内容本身。在这种情况下,标记表示类型和范围,因此确定用于表示数据大小的字节数。大小本身编码为 8 位、16 位或 32 位无符号整数。不支持比这更长的尺寸。

下图说明了大小可变的值的一般布局,此处使用 16 位大小

packstream sized value

字节序

PackStream 专门使用大端表示法。这意味着值的最高有效部分首先写入网络或内存空间,最低有效部分最后写入。

数据类型

Null

标记: C0

Null 始终使用单个标记字节 C0 编码。

Boolean

标记,false: C2

标记,true: C3

布尔值在单个标记字节中编码,使用 C3 表示,使用 C2 表示

Integer

标记,TINY_INT

标记 十进制数

F0

-16

F1

-15

F2

-14

F3

-13

F4

-12

F5

-11

F6

-10

F7

-9

F8

-8

F9

-7

FA

-6

FB

-5

FC

-4

FD

-3

FE

-2

FF

-1

00

0

01

1

02

2

…​

…​

…​

…​

…​

…​

7E

126

7F

127

标记,INT_8 C8

标记,INT_16 C9

标记,INT_32 CA

标记,INT_64 CB

整数值占用 1、2、3、5 或 9 个字节,具体取决于大小。可用的表示形式为

表示形式 大小(字节) 描述

TINY_INT

1

仅标记字节

INT_8

2

标记字节 C8 后跟带符号的 8 位整数

INT_16

3

标记字节 C9 后跟带符号的 16 位整数

INT_32

5

标记字节 CA 后跟带符号的 32 位整数

INT_64

9

标记字节 CB 后跟带符号的 64 位整数

下图说明了可用的编码,每个编码都显示了十进制值 42 的有效表示形式

表示形式 大小(字节) 描述

TINY_INT

1

2A

INT_8

2

C8 2A

INT_16

3

C9 00 2A

INT_32

5

CA 00 00 00 2A

INT_64

9

CB 00 00 00 00 00 00 00 2A

一些标记字节可用于承载小整数的值及其类型。这些标记可以通过零高位(对于正值)或仅包含 1 的高位 nibble(对于负值)来识别。具体来说,值 007F(包括 007F)可以与具有相同值的正整数直接转换。同样,值 F0FF(包括 F0FF)可以对 -16 到 -1 之间的负数执行相同的操作。

虽然可以使用更宽的格式来编码小数字,但通常建议使用尽可能紧凑的表示形式。

下表显示了带符号 64 位范围内每个可能整数的最佳表示形式

范围最小值 范围最大值 最佳表示形式

-9 223 372 036 854 775 808

-2 147 483 649

INT_64

-2 147 483 648

-32 769

INT_32

-32 768

-129

INT_16

-128

-17

INT_8

-16

+127

TINY_INT

+128

+32 767

INT_16

+32 768

+2 147 483 647

INT_32

+2 147 483 648

+9 223 372 036 854 775 807

INT_64

使用最小值的示例

-9223372036854775808(最小值)可以表示为

CB 80 00 00 00 00 00 00 00
使用最大值的示例

9223372036854775807(最大值)可以表示为

CB 7F FF FF FF FF FF FF FF

Float

标记: C1

浮点数是双精度浮点数,通常用于表示分数和小数。它们被编码为一个 C1 标记字节,后跟 8 个字节,这些字节根据 IEEE 754 浮点数“双格式”位布局以大端顺序格式化。

  • 第 63 位(由掩码 0x8000000000000000 选择的位)表示数字的符号。

  • 第 62-52 位(由掩码 0x7ff0000000000000 选择的位)表示指数。

  • 第 51-0 位(由掩码 0x000fffffffffffff 选择的位)表示数字的尾数(有时称为尾数)。

使用十进制值的示例

十进制值 1.23 可以表示为

C1 3F F3 AE 14 7A E1 47 AE

Bytes

字节数组是字节值的数组。这些用于传输原始二进制数据,并且大小表示包含的字节数。与其他值不同,没有单独的编码用于包含少于 16 个字节的字节数组。

标记 大小 最大大小

CC

8 位大端无符号整数

255 字节

CD

16 位大端无符号整数

65 535 字节

CE

32 位大端无符号整数

2 147 483 647 字节

应使用 CCCDCE 之一的标记,具体取决于范围。此标记后跟大小和字节本身。

注意:虽然 CE 后面的 32 位无符号整数可以容纳更大的数字,但字节数组的最大大小限制为 2 147 483 647(带符号 32 位整数的最大值)。

使用空字节数组的示例

空字节数组 b[]

CC 00
使用字节数组中三个值的示例

包含三个值 1、2 和 3 的字节数组;b[1, 2, 3]

CC 03 01 02 03

字符串 (String)

标记符

用于较短的字符串

标记 大小(字节)

80

0

81

1

82

2

83

3

84

4

85

5

86

6

87

7

88

8

89

9

8A

10

8B

11

8C

12

8D

13

8E

14

8F

15

用于较长的字符串

标记 大小 最大字节数

D0

8 位大端无符号整数

255 字节

D1

16 位大端无符号整数

65 535 字节

D2

32 位大端无符号整数

2 147 483 647 字节

文本数据以 **UTF-8** 编码的字节表示。

字符串表示中使用的尺寸是 UTF-8 编码数据的字节数,而不是原始文本的字符数。

对于包含少于 16 个字节的编码文本(包括空字符串),标记字节应包含高位 nibble 8(二进制 1000)以及包含大小的低位 nibble。编码数据紧随标记符之后。对于包含 16 个字节或更多字节的编码文本,应根据规模使用标记符 D0D1D2。此标记符后跟大小和 UTF-8 编码数据。

注意:虽然 D3 后面的无符号 32 位整数可以容纳更大的数字,但字符串的最大字节大小限制为 2,147,483,647(有符号 32 位整数的最大值)。

表 2. 不同字符串的示例
编码

String("")

80

String("A")

81 41

String("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

D0 1A 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A

String("Größenmaßstäbe")

D0 12 47 72 C3 B6 C3 9F 65 6E 6D 61 C3 9F 73 74 C3 A4 62 65

列表 (List)

列表是值的异构序列,因此允许在同一列表中混合使用多种类型。列表的大小表示该列表中项目的数量,而不是打包的总字节大小。

标记符

标记 大小(项目数) 最大尺寸

90

标记符的低位 nibble

0 个项目

91

标记符的低位 nibble

1 个项目

92

标记符的低位 nibble

2 个项目

93

标记符的低位 nibble

3 个项目

94

标记符的低位 nibble

4 个项目

95

标记符的低位 nibble

5 个项目

96

标记符的低位 nibble

6 个项目

97

标记符的低位 nibble

7 个项目

98

标记符的低位 nibble

8 个项目

99

标记符的低位 nibble

9 个项目

9A

标记符的低位 nibble

10 个项目

9B

标记符的低位 nibble

11 个项目

9C

标记符的低位 nibble

12 个项目

9D

标记符的低位 nibble

13 个项目

9E

标记符的低位 nibble

14 个项目

9F

标记符的低位 nibble

15 个项目

D4

8 位大端无符号整数

255 个项目

D5

16 位大端无符号整数

65,535 个项目

D6

32 位大端无符号整数

2,147,483,647 个项目

对于包含少于 16 个项目的列表(包括空列表),标记字节应包含高位 nibble 9(二进制 1001)以及包含大小的低位 nibble。列表中的项目然后按顺序序列化,紧随标记符之后。

对于包含 16 个或更多项目的列表,应根据规模使用标记符 D4D5D6。此标记符后跟大小和按顺序序列化的列表项目。

注意:虽然 D6 后面的无符号 32 位整数可以容纳更大的数字,但列表的最大大小限制为 2,147,483,647(有符号 32 位整数的最大值)。

包含 0 个项目的示例
[]
90
包含 3 个项目的示例
[Integer(1), Integer(2), Integer(3)]
93 01 02 03
包含三种不同类型项目的示例
[
  Integer(1),
  Float(2.0),
  String("three")
]
93
01
C1 40 00 00 00 00 00 00 00
85 74 68 72 65 65
包含超过 15 个项目的示例
[
    Integer(1),
    Integer(2),
    ...
    Integer(40)
]
D4 28
01 02 03 04 05 06 07 08 09 0A
0B 0C 0D 0E 0F 10 11 12 13 14
15 16 17 18 19 1A 1B 1C 1D 1E
1F 20 21 22 23 24 25 26 27 28

字典 (Dictionary)

字典是一个包含键值对的列表。

  • 键必须是 字符串 (String)

  • 可以包含同一键的多个实例。

  • 允许混合使用多种类型。

字典的大小表示该字典中键值对的数量,而不是打包的总字节大小。

标记符

标记 大小(键值对数) 最大尺寸

A0

包含在标记符的低位 nibble 中

0

A1

包含在标记符的低位 nibble 中

1

A2

包含在标记符的低位 nibble 中

2

A3

包含在标记符的低位 nibble 中

3

A4

包含在标记符的低位 nibble 中

4

A5

包含在标记符的低位 nibble 中

5

A6

包含在标记符的低位 nibble 中

6

A7

包含在标记符的低位 nibble 中

7

A8

包含在标记符的低位 nibble 中

8

A9

包含在标记符的低位 nibble 中

9

AA

包含在标记符的低位 nibble 中

10

AB

包含在标记符的低位 nibble 中

11

AC

包含在标记符的低位 nibble 中

12

AD

包含在标记符的低位 nibble 中

13

AE

包含在标记符的低位 nibble 中

14

AF

包含在标记符的低位 nibble 中

15

D8

8 位大端无符号整数

255 个条目

D9

16 位大端无符号整数

65,535 个条目

DA

32 位大端无符号整数

2,147,483,647 个条目

对于包含少于 16 个键值对的字典(包括空字典),标记字节应包含高位 nibble A(二进制 1010)以及包含大小的低位 nibble。

字典中的条目然后按 [键, 值, 键, 值] 顺序序列化,紧随标记符之后。

键始终为 字符串 (String) 值。

对于包含 16 个或更多键值对的字典,应根据规模使用标记符 D8D9DA。此标记符后跟大小和键值对。

注意:虽然 DA 后面的无符号 32 位整数可以容纳更大的数字,但字典的最大大小限制为 2,147,483,647(有符号 32 位整数的最大值)。

包含空字典的示例
{}
A0
包含两个条目的示例
{"one": "eins"}
A1 83 6F 6E 65 84 65 69 6E 73
包含超过 15 个条目的示例
{"A": 1, "B": 2 ... "Z": 26}
D8 1A
81 41 01 81 42 02 81 43 03 81 44 04
81 45 05 81 46 06 81 47 07 81 48 08
81 49 09 81 4A 0A 81 4B 0B 81 4C 0C
81 4D 0D 81 4E 0E 81 4F 0F 81 50 10
81 51 11 81 52 12 81 53 13 81 54 14
81 55 15 81 56 16 81 57 17 81 58 18
81 59 19 81 5A 1A

如果解包时存在同一键的多个实例,则应使用该键的最后一次看到的值。

示例
[("key_1", 1), ("key_2", 2), ("key_1", 3)] -> {"key_1": 3, "key_2": 2}

结构体 (Structure)

结构体是复合值,由字段和唯一的类型代码组成。除了标记符之外,结构体编码还包括一个字节(**标签字节**),后跟最多 15 个字段的序列,每个字段都是一个单独的值。结构体的大小以字段数衡量,而不是总字节大小。此计数不包括标签。

标记符

标记 大小(字段数) 最大尺寸

B0

包含在标记符的低位 nibble 中

0 个字段

B1

包含在标记符的低位 nibble 中

1 个字段

B2

包含在标记符的低位 nibble 中

2 个字段

B3

包含在标记符的低位 nibble 中

3 个字段

B4

包含在标记符的低位 nibble 中

4 个字段

B5

包含在标记符的低位 nibble 中

5 个字段

B6

包含在标记符的低位 nibble 中

6 个字段

B7

包含在标记符的低位 nibble 中

7 个字段

B8

包含在标记符的低位 nibble 中

8 个字段

B9

包含在标记符的低位 nibble 中

9 个字段

BA

包含在标记符的低位 nibble 中

10 个字段

BB

包含在标记符的低位 nibble 中

11 个字段

BC

包含在标记符的低位 nibble 中

12 个字段

BD

包含在标记符的低位 nibble 中

13 个字段

BE

包含在标记符的低位 nibble 中

14 个字段

BF

包含在标记符的低位 nibble 中

15 个字段

对于包含少于 16 个字段的结构体,标记字节应包含高位 nibble B(二进制 1011)以及包含大小的低位 nibble。标记符后紧跟**标签字节**和字段值,顺序排列。**标签字节**用于标识结构体的类型或类别,并且可以保存 0 到 +127 之间的任何值。

PackStream 本身不定义不同结构体的语义。相反,请参阅结构体语义以了解相关 Bolt 版本。