Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 21 additions & 22 deletions src/main/java/com/yomahub/roguemap/RogueMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,19 @@ private RogueMap(Index<K> index, StorageEngine storage,
*/
public V put(K key, V value) {
if (key == null) {
throw new IllegalArgumentException("键不能为 null");
throw new IllegalArgumentException("Key cannot be null");
}

// 计算所需大小
int valueSize = valueCodec.calculateSize(value);
if (valueSize < 0) {
throw new IllegalStateException("无法确定值的大小");
throw new IllegalStateException("Cannot determine value size");
}

// 为值分配内存
long newAddress = allocator.allocate(valueSize);
if (newAddress == 0) {
throw new OutOfMemoryError("分配 " + valueSize + " 字节失败");
long address = allocator.allocate(valueSize);
if (address == 0) {
throw new OutOfMemoryError("Failed to allocate " + valueSize + " bytes");
}

try {
Expand Down Expand Up @@ -167,13 +167,12 @@ public boolean isEmpty() {
* 移除所有条目
*/
public void clear() {
// 遍历所有条目并释放内存
index.forEach((key, address, size) -> {
allocator.free(address, size);
// 使用新添加的 clear(Consumer) 方法,在清除索引的同时释放堆外内存
index.clear((address, size) -> {
if (address != 0) {
allocator.free(address, size);
}
});

// 清空索引
index.clear();
}

/**
Expand Down Expand Up @@ -333,7 +332,7 @@ public B valueCodec(Codec<V> valueCodec) {
*/
public B initialCapacity(int initialCapacity) {
if (initialCapacity <= 0) {
throw new IllegalArgumentException("initialCapacity 必须为正数");
throw new IllegalArgumentException("initialCapacity must be positive");
}
this.initialCapacity = initialCapacity;
return (B) this;
Expand Down Expand Up @@ -393,7 +392,7 @@ protected Index<K> createIndexFromType(int indexType, Codec<K> keyCodec) {
} else if (indexType == 3) {
return (Index<K>) new IntPrimitiveIndex(initialCapacity);
}
throw new IllegalStateException("未知的索引类型: " + indexType);
throw new IllegalStateException("Unknown index type: " + indexType);
}

/**
Expand All @@ -412,7 +411,7 @@ protected Index<K> createNewIndex(Codec<K> keyCodec) {
return (Index<K>) new IntPrimitiveIndex(initialCapacity);
} else {
throw new IllegalStateException(
"原始类型索引仅支持 Long Integer 键,请使用 PrimitiveCodecs.LONG PrimitiveCodecs.INTEGER");
"Primitive index only supports Long or Integer keys, please use PrimitiveCodecs.LONG or PrimitiveCodecs.INTEGER" );
}
} else if (useSegmentedIndex) {
return new SegmentedHashIndex<>(keyCodec, segmentCount, initialCapacity);
Expand Down Expand Up @@ -451,7 +450,7 @@ private MmapBuilder() {
*/
public MmapBuilder<K, V> persistent(String filePath) {
if (filePath == null || filePath.isEmpty()) {
throw new IllegalArgumentException("文件路径不能为空");
throw new IllegalArgumentException("File path cannot be empty");
}
this.persistentFilePath = filePath;
this.isTemporary = false;
Expand All @@ -478,7 +477,7 @@ public MmapBuilder<K, V> temporary() {
*/
public MmapBuilder<K, V> allocateSize(long size) {
if (size <= 0) {
throw new IllegalArgumentException("分配大小必须为正数");
throw new IllegalArgumentException("Allocate size must be positive");
}
this.allocateSize = size;
return this;
Expand All @@ -487,15 +486,15 @@ public MmapBuilder<K, V> allocateSize(long size) {
@Override
public RogueMap<K, V> build() {
if (keyCodec == null) {
throw new IllegalStateException("必须设置键编解码器");
throw new IllegalStateException("Key codec must be set");
}
if (valueCodec == null) {
throw new IllegalStateException("必须设置值编解码器");
throw new IllegalStateException("Value codec must be set");
}

// 临时文件模式不需要指定路径
if (!isTemporary && (persistentFilePath == null || persistentFilePath.isEmpty())) {
throw new IllegalStateException("MMAP 模式必须设置文件路径,请使用 persistent(filePath) temporary()");
throw new IllegalStateException("MMAP mode must set file path, please use persistent(filePath) or temporary()");
}

// 创建 MmapAllocator(临时模式会自动生成文件路径)
Expand Down Expand Up @@ -555,7 +554,7 @@ private OffHeapBuilder() {
*/
public OffHeapBuilder<K, V> maxMemory(long maxMemory) {
if (maxMemory <= 0) {
throw new IllegalArgumentException("maxMemory 必须为正数");
throw new IllegalArgumentException("maxMemory must be positive");
}
this.maxMemory = maxMemory;
return this;
Expand All @@ -564,10 +563,10 @@ public OffHeapBuilder<K, V> maxMemory(long maxMemory) {
@Override
public RogueMap<K, V> build() {
if (keyCodec == null) {
throw new IllegalStateException("必须设置键编解码器");
throw new IllegalStateException("Key codec must be set");
}
if (valueCodec == null) {
throw new IllegalStateException("必须设置值编解码器");
throw new IllegalStateException("Value codec must be set");
}

// 堆外内存模式
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/yomahub/roguemap/func/EntryConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.yomahub.roguemap.func;

@FunctionalInterface
public interface EntryConsumer {
void accept(long address, int size);
}
47 changes: 35 additions & 12 deletions src/main/java/com/yomahub/roguemap/index/HashIndex.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.yomahub.roguemap.index;

import com.yomahub.roguemap.func.EntryConsumer;
import com.yomahub.roguemap.memory.UnsafeOps;
import com.yomahub.roguemap.serialization.Codec;

Expand Down Expand Up @@ -39,10 +40,10 @@ public HashIndex(Codec<K> keyCodec, int initialCapacity) {
@Override
public long put(K key, long address, int valueSize) {
if (key == null) {
throw new IllegalArgumentException("键不能为 null");
throw new IllegalArgumentException("Key cannot be null");
}
if (address == 0) {
throw new IllegalArgumentException("无效的地址: 0");
throw new IllegalArgumentException("Invalid address: 0");
}

Entry newEntry = new Entry(address, valueSize);
Expand Down Expand Up @@ -147,8 +148,20 @@ public int size() {

@Override
public void clear() {
map.clear();
size.set(0);
clear(null);
}

@Override
public void clear(EntryConsumer action) {
// 使用迭代器逐个移除,确保原子性和回调执行
map.forEach((k, v) -> {
if (map.remove(k, v)) { // 原子性移除
size.decrementAndGet();
if (action != null) {
action.accept(v.address, v.size);
}
}
});
}

@Override
Expand All @@ -159,7 +172,7 @@ public void close() {
@Override
public int serializedSize() {
if (keyCodec == null) {
throw new UnsupportedOperationException("无法序列化:keyCodec null");
throw new UnsupportedOperationException("Serialization not supported: keyCodec is null");
}

int totalSize = 4; // entry count (4 bytes)
Expand All @@ -168,7 +181,7 @@ public int serializedSize() {
K key = entry.getKey();
int keySize = keyCodec.calculateSize(key);
if (keySize < 0) {
throw new IllegalStateException("键的大小不能为负数");
throw new IllegalStateException("Key size cannot be negative");
}
// 4 bytes (key size) + key bytes + 8 bytes (address) + 4 bytes (size)
totalSize += 4 + keySize + 8 + 4;
Expand All @@ -180,7 +193,7 @@ public int serializedSize() {
@Override
public int serialize(long address) {
if (keyCodec == null) {
throw new UnsupportedOperationException("无法序列化:keyCodec null");
throw new UnsupportedOperationException("Serialization not supported: keyCodec is null");
}

long currentAddr = address;
Expand All @@ -196,7 +209,7 @@ public int serialize(long address) {
// 计算键大小并编码
int keySize = keyCodec.calculateSize(key);
if (keySize < 0) {
throw new IllegalStateException("键的大小不能为负数");
throw new IllegalStateException("Key size cannot be negative");
}

// 写入 key size
Expand All @@ -222,7 +235,7 @@ public int serialize(long address) {
@Override
public void deserialize(long address, int totalSize) {
if (keyCodec == null) {
throw new UnsupportedOperationException("无法反序列化:keyCodec null");
throw new UnsupportedOperationException("Deserialization not supported: keyCodec is null");
}

map.clear();
Expand Down Expand Up @@ -260,7 +273,7 @@ public void deserialize(long address, int totalSize) {
@Override
public int serializeWithOffsets(long address, long baseAddress) {
if (keyCodec == null) {
throw new UnsupportedOperationException("无法序列化:keyCodec null");
throw new UnsupportedOperationException("Serialization not supported: keyCodec is null");
}

long currentAddr = address;
Expand All @@ -276,7 +289,7 @@ public int serializeWithOffsets(long address, long baseAddress) {
// 计算键大小并编码
int keySize = keyCodec.calculateSize(key);
if (keySize < 0) {
throw new IllegalStateException("键的大小不能为负数");
throw new IllegalStateException("Key size cannot be negative");
}

// 写入 key size
Expand All @@ -303,7 +316,7 @@ public int serializeWithOffsets(long address, long baseAddress) {
@Override
public void deserializeWithOffsets(long address, int totalSize, long baseAddress) {
if (keyCodec == null) {
throw new UnsupportedOperationException("无法反序列化:keyCodec null");
throw new UnsupportedOperationException("Deserialization not supported: keyCodec is null");
}

map.clear();
Expand Down Expand Up @@ -341,6 +354,16 @@ public void deserializeWithOffsets(long address, int totalSize, long baseAddress
this.size.set(entryCount);
}

@Override
public void forEach(EntryConsumer action) {
if (action == null) {
throw new IllegalArgumentException("Action cannot be null");
}
for (Entry entry : map.values()) {
action.accept(entry.address, entry.size);
}
}

/**
* Entry 保存值的内存地址和大小
*/
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/yomahub/roguemap/index/Index.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.yomahub.roguemap.index;

import com.yomahub.roguemap.func.EntryConsumer;

/**
* 索引接口,用于键值查找
*
Expand Down Expand Up @@ -107,6 +109,13 @@ public interface Index<K> {
*/
void clear();

/**
* 从索引中移除所有条目,并对每个移除的条目执行操作
*
* @param action 对每个被移除条目执行的操作 (address, size)
*/
void clear(EntryConsumer action);

/**
* 关闭索引并释放资源
*/
Expand Down Expand Up @@ -152,4 +161,11 @@ public interface Index<K> {
* @param baseAddress 基础地址,用于重新计算内存地址
*/
void deserializeWithOffsets(long address, int size, long baseAddress);

/**
* 遍历索引中的所有条目
*
* @param action 对每个条目执行的操作 (address, size)
*/
void forEach(EntryConsumer action);
}
46 changes: 40 additions & 6 deletions src/main/java/com/yomahub/roguemap/index/IntPrimitiveIndex.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package com.yomahub.roguemap.index;

import com.yomahub.roguemap.func.EntryConsumer;
import com.yomahub.roguemap.memory.UnsafeOps;
import java.util.concurrent.locks.StampedLock;

/**
* 极致优化的Integer键索引 - 使用原始类型数组
*
* <p>
* 内存占用(100万条,负载因子0.75):
* - keys: 4 bytes × 1,333,333 = 5.3 MB
* - addresses: 8 bytes × 1,333,333 = 10.7 MB
* - sizes: 4 bytes × 1,333,333 = 5.3 MB
* - 总计: ~21.3 MB(实际存储100万条约16MB)
*
* <p>
* 相比Long键索引,再节省25%内存
*/
public class IntPrimitiveIndex implements Index<Integer> {
Expand Down Expand Up @@ -45,10 +46,10 @@ public IntPrimitiveIndex(int initialCapacity) {
@Override
public long put(Integer key, long address, int valueSize) {
if (key == null || key == EMPTY_KEY || key == DELETED_KEY) {
throw new IllegalArgumentException("无效的键: " + key);
throw new IllegalArgumentException("Invalid key: " + key);
}
if (address == 0) {
throw new IllegalArgumentException("无效的地址: 0");
throw new IllegalArgumentException("Invalid address: 0");
}

long stamp = lock.writeLock();
Expand Down Expand Up @@ -245,8 +246,21 @@ public int size() {

@Override
public void clear() {
clear(null);
}

@Override
public void clear(EntryConsumer action) {
long stamp = lock.writeLock();
try {
if (action != null) {
for (int i = 0; i < keys.length; i++) {
int key = keys[i];
if (key != EMPTY_KEY && key != DELETED_KEY) {
action.accept(addresses[i], sizes[i]);
}
}
}
keys = new int[DEFAULT_CAPACITY];
addresses = new long[DEFAULT_CAPACITY];
sizes = new int[DEFAULT_CAPACITY];
Expand Down Expand Up @@ -277,13 +291,33 @@ public int serializedSize() {
@Override
public int serialize(long address) {
// 原始类型索引暂不支持序列化
throw new UnsupportedOperationException("IntPrimitiveIndex 暂不支持序列化");
throw new UnsupportedOperationException("IntPrimitiveIndex does not support serialization temporarily");
}

@Override
public void deserialize(long address, int size) {
// 原始类型索引暂不支持序列化
throw new UnsupportedOperationException("IntPrimitiveIndex 暂不支持序列化");
throw new UnsupportedOperationException("IntPrimitiveIndex does not support serialization temporarily");
}

@Override
public void forEach(EntryConsumer action) {
if (action == null) {
throw new IllegalArgumentException("Action cannot be null");
}
long stamp = lock.readLock();
try {
for (int i = 0; i < keys.length; i++) {
int key = keys[i];
if (key != EMPTY_KEY && key != DELETED_KEY) {
long addr = addresses[i];
int sz = sizes[i];
action.accept(addr, sz);
}
}
} finally {
lock.unlockRead(stamp);
}
}

private int probe(int key) {
Expand Down
Loading