基本信息
| 属性 |
值 |
| 实现类 |
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、释放扇区 |
删文件 + 清位 |
相关条目