PackStream

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

版本 1

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

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

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

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

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

Null (空值)

缺失或空值

Boolean (布尔值)

truefalse

Integer (整数)

有符号 64 位整数

Float (浮点数)

64 位浮点数

Bytes (字节数组)

字节数组

String (字符串)

Unicode 文本,UTF-8

List (列表)

有序值集合

Dictionary (字典)

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

Structure (结构体)

具有类型签名的复合值

不包括无符号整数和 32 位浮点数。这是一个有意的设计决策,旨在实现客户端语言之间的更广泛兼容性。

通用表示

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

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

某些值(例如布尔真)可以编码在一个标记字节内。许多小整数(特别是介于 -16 和 +127 之间(含))也编码在一个字节内。

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

带大小的值

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

下图说明了带大小值的通用布局,此处为 16 位大小

packstream sized value

字节序

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

数据类型

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

一些标记字节可用于承载小整数的值及其类型。这些标记可以通过高位为零(对于正值)或高位半字节仅包含一(对于负值)来识别。具体来说,介于 007F 之间(含)的值可以直接转换为与它们值相同的正整数,反之亦然。类似地,介于 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 字节的编码文本(包括空字符串),标记字节应包含高位半字节 8(二进制 1000),后跟包含大小的低位半字节。编码数据紧随标记之后。对于包含 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

标记的低位半字节

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 个或更多项目的列表,应根据比例使用标记 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

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 个或更多键值对的字典,应根据比例使用标记 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

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

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 版本的结构体语义

© . All rights reserved.