概述
本文档规定了 I2P blockfile 文件格式以及 Blockfile 命名服务 NAMING 使用的 hostsdb.blockfile 中的表格。
blockfile 以紧凑格式提供快速的 Destination 查找。虽然 blockfile 页面开销很大,但目标地址以二进制格式存储,而不是像 hosts.txt 格式那样使用 Base 64。此外,blockfile 还提供为每个条目存储任意元数据(如添加日期、来源和注释)的功能。元数据可能在未来用于提供高级地址簿功能。blockfile 的存储需求相比 hosts.txt 格式只是适度增加,但 blockfile 的查找时间减少了大约 10 倍。
blockfile 是多个排序映射(键值对)的简单磁盘存储,实现为跳表。blockfile 格式采用自 Metanotion Blockfile Database METANOTION 。首先我们将定义文件格式,然后说明 BlockfileNamingService 对该格式的使用。
块文件格式
原始的 blockfile 规范经过修改,为每个页面添加了魔数。文件以 1024 字节的页面结构组织。页面编号从 1 开始。“超级块"始终位于第 1 页,即文件中从字节 0 开始的位置。元索引跳跃表始终位于第 2 页,即文件中从字节 1024 开始的位置。
所有 2 字节整数值都是无符号的。所有 4 字节整数值(页码)都是有符号的,负值是非法的。所有整数值都以网络字节序(大端序)存储。
该数据库设计为由单个线程打开和访问。BlockfileNamingService 提供同步机制。
超级块格式
| Byte | Contents | Description |
|---|---|---|
| 0-5 | Magic number | 0x3141de493250 ("1A" 0xde "I2P") |
| 6 | Major version | 0x01 |
| 7 | Minor version | 0x02 |
| 8-15 | File length | Total length in bytes |
| 16-19 | First free list page | |
| 20-21 | Mounted flag | 0x01 = yes |
| 22-23 | Span size | Max number of key/value pairs per span (16 for hostsdb). Used for new skip lists. |
| 24-27 | Page size | As of version 1.2. Prior to 1.2, 1024 is assumed. |
| 28-1023 | unused |
| Byte | Contents | Description |
|---|---|---|
| 0-7 | Magic number | 0x536b69704c697374 "SkipList" |
| 8-11 | First span page | |
| 12-15 | First level page | |
| 16-19 | Size | Total number of keys - may only be valid at startup |
| 20-23 | Spans | Total number of spans - may only be valid at startup |
| 24-27 | Levels | Total number of levels - may only be valid at startup |
| 28-29 | Span size | As of version 1.2. Max number of key/value pairs per span. Prior to that, specified for all skiplists in the superblock. Used for new spans in this skip list. |
| 30-1023 | unused |
所有级别都有跨度。但不是所有跨度都有级别。
| Byte | Contents | Description |
|---|---|---|
| 0-7 | Magic number | 0x42534c6576656c73 "BSLevels" |
| 8-9 | Max height | |
| 10-11 | Current height | |
| 12-15 | Span page | |
| 16- | Next level pages | 'current height' entries, 4 bytes each, lowest first |
| remaining | unused |
键值结构在每个跨度内以及跨所有跨度都按键排序。键值结构在每个跨度内按键排序。除第一个跨度外的其他跨度不能为空。
| Byte | Contents | Description |
|---|---|---|
| 0-3 | Magic number | 0x5370616e "Span" |
| 4-7 | First continuation page | or 0 |
| 8-11 | Previous span page | or 0 |
| 12-15 | Next span page | or 0 |
| 16-17 | Max keys | 16 for hostsdb |
| 18-19 | Size | Current number of keys |
| 20-1023 | key/value structures |
| Byte | Contents | Description |
|---|---|---|
| 0-3 | Magic number | 0x434f4e54 "CONT" |
| 4-7 | Next continuation page | or 0 |
| 8-1023 | key/value structures |
键和值的长度不能跨页分割,即所有4个字节必须在同一页上。如果空间不足,页面的最后1-3个字节将不被使用,长度将位于续页的偏移量8处。键和值数据可以跨页分割。键和值的最大长度为65535字节。
| Byte | Contents |
|---|---|
| 0-1 | key length in bytes |
| 2-3 | value length in bytes |
| 4- | key data |
| value data |
| Byte | Contents | Description |
|---|---|---|
| 0-7 | Magic number | 0x2366724c69737423 "#frList#" |
| 8-11 | Next free list block | or 0 if none |
| 12-15 | Number of valid free pages | in this block (0 - 252) |
| 16-1023 | Free pages | 4 bytes each, only the first (valid number) are valid |
| Byte | Contents | Description |
|---|---|---|
| 0-7 | Magic number | 0x7e2146524545217e "~!FREE!~" |
| 8-1023 | unused |
Blockfile 命名服务表
BlockfileNamingService 创建和使用的表如下所示。每个跨度的最大条目数为 16。
属性跳表
%%__INFO__%% 是主要的数据库跳表,包含字符串/属性键值对条目,仅包含一个条目:
info - 一个 Properties(UTF-8 字符串/字符串映射),序列化为 Mapping :
- version - “4”
- created - Java long 时间 (毫秒)
- upgraded - Java long 时间 (毫秒) (数据库版本 2 起)
- lists - 逗号分隔的主机数据库列表,按顺序搜索以进行查找。几乎总是 “privatehosts.txt,userhosts.txt,hosts.txt”。
- listversion_* - lists 中每个数据库的版本,例如:listversion_hosts.txt=4。用于识别单个列表的部分或中止升级。(数据库版本 4 起)
反向查找跳表
%%__REVERSE__%% 是反向查找跳跃表,包含整数/属性键值对条目(从数据库版本2开始):
- skiplist 键是 4 字节整数,为 Destination 哈希值的前 4 个字节。
- skiplist 值分别是一个 Properties(UTF-8 字符串/字符串映射表),序列化为 Mapping
- properties 中可能有多个条目,每个都是反向映射,因为给定的 destination 可能有多个主机名,或者哈希值的前 4 个字节可能发生冲突。
- 每个属性键是一个主机名。
- 每个属性值是空字符串。
hosts.txt、userhosts.txt 和 privatehosts.txt 跳过列表
对于每个主机数据库,都有一个包含该数据库主机的跳跃表。请注意,版本4格式支持每个主机名对应多个Destinations。此格式在I2P版本0.9.26中引入。版本3数据库会自动迁移到版本4。
这些跳跃表中的键/值如下:
key - 一个 UTF-8 字符串(主机名)
value - - 数据库版本4:一个DestEntry,它是一个单字节数字,表示后续要跟随的Properties/Destination对的数量。该数量的对数包括:一个Properties(UTF-8字符串/字符串映射),序列化为Mapping ,后跟一个二进制Destination (按常规方式序列化)。- 数据库版本3:一个DestEntry,它是一个Properties(UTF-8字符串/字符串映射),序列化为Mapping ,后跟一个二进制Destination (按常规方式序列化)。
DestEntry Properties 通常包含:
- “a” - 添加时间(Java long 时间,以毫秒为单位)
- “m” - 最后修改时间(Java long 时间,以毫秒为单位)
- “notes” - 用户提供的注释
- “s” - 条目的原始来源(通常是文件名或订阅 URL)
- “v” - 如果条目的签名已验证,则为 “true” 或 “false”
主机名密钥以小写形式存储,并且始终以”.i2p"结尾。