PackStream

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

版本 1

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

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

PackStream 提供了一系列核心数据类型,其中许多类型支持多种二进制表示,并具备灵活的扩展机制。

核心数据类型如下表所示。

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

Null

缺失或空值

布尔值

true 或 false

整数

带符号 64 位整数

浮点数

64 位浮点数

Bytes

字节数组

字符串

Unicode 文本,UTF-8

列表

值的有序集合

Dictionary

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

Structure

带类型签名的复合值

不包含无符号整数和 32 位浮点数。这是一个深思熟虑的设计决策,旨在允许跨客户端语言实现更广泛的兼容性。

通用表示

每个序列化的 PackStream 值都以一个标记字节 (marker byte) 开头。

该标记包含数据类型信息,以及对于需要该信息的类型,包含直接或间接的大小信息。大小信息的编码方式因标记类型而异。

某些值(例如布尔值 true)可以在单个标记字节内编码。许多小整数(具体为 -16 到 +127 之间)也编码在单个字节内。

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

定长值 (Sized values)

某些表示具有可变长度,并在表示中显式编码了其大小。此类值通常以一个标记字节开头,后跟一个大小,再后跟数据内容本身。在此上下文中,标记同时表示类型和规模,因此决定了用于表示数据大小的字节数。大小本身编码为 8 位、16 位或 32 位无符号整数。不支持超过此长度的大小。

下图展示了定长值的一般布局,此处以 16 位大小为例

packstream sized value

字节序 (Endianness)

PackStream 仅使用大端序 (big-endian) 表示。这意味着值的最高有效部分首先被写入网络或内存空间,而最低有效部分最后被写入。

数据类型

Null

标记: C0

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

Boolean

标记, false: C2

标记, true: C3

布尔值在单个标记字节内编码,使用 C3 表示 true,使用 C2 表示 false。

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

某些标记字节既可用于表示小整数的值,也可用于表示其类型。这些标记可以通过高位比特为 0(对于正值)或高位半字节仅包含 1(对于负值)来识别。具体来说,00 到 7F(含)之间的值可以直接转换为相同值的正整数。同样,F0 到 FF(含)之间的值可以同样转换为 -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 选择的位)代表数字的尾数(有时称为 mantissa)。

十进制值示例

十进制值 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 字节

应根据规模使用标记 CC、CD 或 CE 之一。此标记后跟大小和字节本身。

注意:虽然 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 个字节的编码文本(包括空字符串),标记字节应包含高位半字节 8(二进制 1000),后跟包含大小的低位半字节。编码数据紧随标记之后。对于包含 16 个或更多字节的编码文本,应根据规模使用标记 D0、D1 或 D2。此标记后跟大小和 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

标记的低位半字节

0 个项目

91

标记的低位半字节

1 个项目

92

标记的低位半字节

2 个项目

93

标记的低位半字节

3 个项目

94

标记的低位半字节

4 个项目

95

标记的低位半字节

5 个项目

96

标记的低位半字节

6 个项目

97

标记的低位半字节

7 个项目

98

标记的低位半字节

8 个项目

99

标记的低位半字节

9 个项目

9A

标记的低位半字节

10 个项目

9B

标记的低位半字节

11 个项目

9C

标记的低位半字节

12 个项目

9D

标记的低位半字节

13 个项目

9E

标记的低位半字节

14 个项目

9F

标记的低位半字节

15 个项目

D4

8 位大端序无符号整数

255 个项目

D5

16 位大端序无符号整数

65 535 个项目

D6

32 位大端序无符号整数

2 147 483 647 个项目

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

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

注意:虽然 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

Dictionary 是包含键值对条目的列表

  • 键必须是 String

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

  • 允许混合类型

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

标记

标记 大小 (键值对条目) 最大大小

A0

包含在标记的低位半字节中

0

A1

包含在标记的低位半字节中

1

A2

包含在标记的低位半字节中

2

A3

包含在标记的低位半字节中

3

A4

包含在标记的低位半字节中

4

A5

包含在标记的低位半字节中

5

A6

包含在标记的低位半字节中

6

A7

包含在标记的低位半字节中

7

A8

包含在标记的低位半字节中

8

A9

包含在标记的低位半字节中

9

AA

包含在标记的低位半字节中

10

AB

包含在标记的低位半字节中

11

AC

包含在标记的低位半字节中

12

AD

包含在标记的低位半字节中

13

AE

包含在标记的低位半字节中

14

AF

包含在标记的低位半字节中

15

D8

8 位大端序无符号整数

255 条目

D9

16 位大端序无符号整数

65 535 条目

DA

32 位大端序无符号整数

2 147 483 647 条目

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

字典中的条目随后按 [key, value, key, value] 的顺序序列化在标记之后。

键始终是 String 值。

对于包含 16 个或更多键值对条目的字典,应根据规模使用标记 D8、D9 或 DA。此标记后跟大小和键值对条目。

注意:虽然 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

包含在标记的低位半字节中

0 个字段

B1

包含在标记的低位半字节中

1 个字段

B2

包含在标记的低位半字节中

2 个字段

B3

包含在标记的低位半字节中

3 个字段

B4

包含在标记的低位半字节中

4 个字段

B5

包含在标记的低位半字节中

5 个字段

B6

包含在标记的低位半字节中

6 个字段

B7

包含在标记的低位半字节中

7 个字段

B8

包含在标记的低位半字节中

8 个字段

B9

包含在标记的低位半字节中

9 个字段

BA

包含在标记的低位半字节中

10 个字段

BB

包含在标记的低位半字节中

11 个字段

BC

包含在标记的低位半字节中

12 个字段

BD

包含在标记的低位半字节中

13 个字段

BE

包含在标记的低位半字节中

14 个字段

BF

包含在标记的低位半字节中

15 个字段

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

PackStream 本身不为不同的结构定义语义。请参阅相关 Bolt 版本的 结构语义 (Structure Semantics)。

© . This site is unofficial and not affiliated with Neo4j, Inc.