.ext 回退格式(ext-region-format)

基本信息

属性 值
实现类 cubicchunks.regionlib.lib.ExtRegion<K extends IKey<K>>
接口 cubicchunks.regionlib.api.region.IRegion<K>
路径 saveDirectory.resolve(regionKey.getName() + ".ext") —— 是一个目录,不是文件
条目文件名 String.valueOf(key.getId()),即一个纯十进制整数(例:1234)
存在性判定 Files.exists(directory.resolve(regionKey.getName() + ".ext"))
条目大小上限 无(只有一个 2 GiB 的 Integer.MAX_VALUE 保护)

存在的唯一理由:打包区域文件的单条目上限是 255 个扇区。SaveSection 用 回退链把写不进去的 key 自动改存成"一个 key 一个文件",绕开该限制。

构造:存在位图

this.exists = new BitSet(keyProvider.getKeyCount(regionKey));
if (!Files.exists(this.directory)) return;   // 目录不存在就保持 initialized = false,不创建

存在时用 Files.list 扫目录,把能 Integer.parseInt 成功、且落在 [0, keyCount) 的文件名对应位设 1;解析失败的名字被静默吞掉(空的 catch (NumberFormatException ex) {})——这正是写入过程中的 <id>.tmp 临时文件不会污染位图的原因。

写入:DSYNC + 原子移动

步骤 细节
快速路径 value == null 且(未初始化 / exists 全空 / 该位为 0)→ 直接返回,不创建目录(注释说明 SaveSection 保证普通写成功后会擦除 ext 区,所以这里不该凭空造目录)
懒初始化 Utils.createDirectories(this.directory);Utils 逐级递归 Files.createDirectory,显式绕开 Files.createDirectories 是因为后者不处理符号链接
临时文件 <id>.tmp
打开选项 WRITE, CREATE, TRUNCATE_EXISTING, DSYNC
一次写完 收集所有 IHeaderDataEntryProvider 产出的头缓冲 + 数据缓冲,交给单个 GatheringByteChannel 的 Utils.writeFully(channel, ByteBuffer[]);DSYNC 保证 channel 关闭时数据已落盘
原子提交 Files.move(tmpFile, file, ATOMIC_MOVE, REPLACE_EXISTING) —— 崩溃时文件不会只写一半
删除分支 文件不存在 + value == null → 只清位;文件存在 + value == null → Files.delete(file) + 清位

头表前缀

ExtRegion 也会写 IHeaderDataEntryProvider 提供的头字段,排在数据前面;读取时直接跳过:

int totalHeaderSize = Σ p.getEntryByteCount();          // 构造时累加
ByteBuffer buf = ByteBuffer.wrap(new byte[(int) (size - totalHeaderSize)]);
Utils.readFully(channel.position(totalHeaderSize), buf);

实际使用中 SaveSection2D / SaveSection3D 都传 Collections.emptyList(),所以 totalHeaderSize == 0,文件里就是裸数据。

读取

情况 行为
!initialized 或位图该位为 0 返回 Optional.empty()
文件已在位图之后被外部删掉 清掉该位并返回 Optional.empty()(自愈)
size > Integer.MAX_VALUE 抛 UnsupportedDataException("Size " + size + " is too big")

不支持的操作

方法 行为
writeSpecial(key, marker) 抛 UnsupportedOperationException("ExtRegion doesn't support special values")
flush() 空实现(每次写都已 DSYNC,没有需要刷的缓冲)
close() 空实现(没有常驻文件描述符)
readValue 非 synchronized(与 Region 不同,它不持有 FileChannel)

与打包格式的对比

特性 打包 Region ExtRegion
物理形态 单个文件 每 key 一个文件的目录
条目大小上限 255 扇区(512 → 130556 B;4096 → 1044476 B) 无(上限 Integer.MAX_VALUE)
头表 文件开头一整块扇区位图 每条目自带前缀(实用中长度 0)
特殊值 支持 抛 UnsupportedOperationException
flush / close 真正落盘 / 关文件 都是 no-op
写入原子性 原地覆写扇区 .tmp + ATOMIC_MOVE
删除语义 头表写 0、释放扇区 删文件 + 清位

相关条目