@orieg /片(expanse-node)
本地Node.js, Bun和Deno绑定 广阔的:洁净室,纯rust Judy数组和256字节的数字尝试,具有乐观并发控制(OCC)。
概述
@orieg/expanse 为JavaScript和TypeScript运行时提供插入式、零依赖、超快速的稀疏数据结构:
ExpanseSet: 64位无符号整数集(compat: Judy1),具有排名/选择和范围查询。ExpanseMap: 64位整型键值映射(compat: JudyL)。ExpanseStrMap:使用前缀压缩的字节字典排序字符串映射(compat: JudySL)。ExpanseBytesMap:任意二进制键的无序映射(包括\0字节)(compat: JudyHS)。ExpanseBlobMap:多态blob存储与内联值打包(≤7字节内联存储与0堆分配),块slab竞技场,就地压缩,和mmap图像持久性。SyncExpanseMap/SyncExpanseSet具有乐观并发控制(OCC)和基于时代的内存回收的线程安全并发结构。SyncExpanseStrMap/SyncExpanseBytesMap/SyncExpanseOrderedBytesMap/SyncExpanseBlobMap:字符串、字节串、有序字节串和blob(字节负载加上24位热元数据)映射具有相同的并发形式。
安装
npm install @orieg/expanse
# or
bun add @orieg/expanse
# or
pnpm add @orieg/expanse
快速入门
64位整数集(ExpanseSet)
import { ExpanseSet } from '@orieg/expanse';
const set = new ExpanseSet();
set.add(42n);
set.add(100n);
set.add(500n);
console.log(set.has(42n)); // true
console.log(set.size()); // 3n
// Rank / Select
console.log(set.rank(100n)); // 1n (1 element strictly < 100)
console.log(set.select(0n)); // 42n (0-th element)
// Navigation
console.log(set.next(42n)); // 100n
console.log(set.prev(500n)); // 100n
// Range queries
console.log(set.countRange(40n, 200n)); // 2n
console.log(set.range(40n, 200n)); // [42n, 100n]
64位整型映射(ExpanseMap)
import { ExpanseMap } from '@orieg/expanse';
const map = new ExpanseMap();
map.set(100n, 9999n);
map.set(200n, 8888n);
console.log(map.get(100n)); // 9999n
console.log(map.first()); // { key: 100n, value: 9999n }
前缀压缩字符串映射(ExpanseStrMap)
import { ExpanseStrMap } from '@orieg/expanse';
const strmap = new ExpanseStrMap();
strmap.set('apple', 1n);
strmap.set('banana', 2n);
strmap.set('cherry', 3n);
console.log(strmap.get('banana')); // 2n
console.log(strmap.first()); // { key: 'apple', value: 1n }
console.log(strmap.next('apple')); // { key: 'banana', value: 2n }
任意二值图(ExpanseBytesMap)
import { ExpanseBytesMap } from '@orieg/expanse';
const bytesmap = new ExpanseBytesMap();
const binaryKey = Buffer.from([0x00, 0x01, 0x02, 0xff]);
bytesmap.set(binaryKey, 42n);
console.log(bytesmap.get(binaryKey)); // 42n
高性能Blob Map (ExpanseBlobMap)
import { ExpanseBlobMap } from '@orieg/expanse';
const blobmap = new ExpanseBlobMap();
// Short payloads (<= 7 bytes) written with hot metadata 0 are stored inline in the
// value slot (0 heap allocations); with metadata they are arena records that keep it
blobmap.set(1n, Buffer.from('inline'), 0);
// Larger payloads are allocated in contiguous slab arenas
blobmap.set(2n, Buffer.from('large payload allocated in slab arena'), 20);
// Lookup with metadata
const res = blobmap.getWithMeta(1n);
console.log(res);
// { payload: <Buffer 69 6e 6c 69 6e 65>, hotMeta: 10, isInline: true }
// Prune based on hot metadata
blobmap.prune((key, hotMeta) => hotMeta > 15);
// Relocatable binary image persistence
blobmap.saveImage('./data.expanse');
const reloaded = ExpanseBlobMap.openImage('./data.expanse');
并发结构(SyncExpanseMap / SyncExpanseSet)
import { SyncExpanseMap, SyncExpanseSet } from '@orieg/expanse';
const syncMap = new SyncExpanseMap();
syncMap.set(1000n, 42n);
console.log(syncMap.get(1000n)); // 42n
console.log(syncMap.next(1000n, true)); // { key: 1000n, value: 42n }
SyncExpanseMap的 get, has, size, first, last, next 和 prev 运行在
乐观路径:除非读耗尽了它的边界,否则它们不使用写锁
重试,在这种情况下,返回到排除写入器的路径。协议是
阻塞乐观锁耦合,而不是无锁。 rank, select, countRange,
keys, values 和 entries的有序读取 SyncExpanseSetrun with:跑
作家排除在外。
SyncExpanseMap 还具有原子读-修改-写功能(#1194)。 compareExchange(key, expected, newValue) 商店 newValue 仅当键的当前值为 expected, null 意思是双方都缺席((null, v) 缺席时插入; (e, null) 移除if =),并返回 { stored, observed }: observed 是比较所看到的值(null 如果不存在),则失败的调用将返回要重试的值。 getOrInsert(key, value) 商店 value 仅当键不存在并返回现有值时,或 null 的约定 set)。 update(key, fn) 商店 fn(current) (null 移除键)并返回被替换的值;这是一个循环 compareExchange 调用 fn 当交换损失时,再次使用观察值,因此 fn 每次调用可以运行多次,并且必须没有副作用,以及来自 fn 在键不变的情况下传播。插件的对象属于一个JavaScript线程,因此交换只会在读取和交换之间进行写操作时丢失(例如从内部) fn)。
syncMap.update(7n, (cur) => (cur ?? 0n) + 1n); // atomic increment
syncMap.compareExchange(7n, 1n, null); // { stored: true, observed: 1n }: remove if equal to 1n
并发字符串、字节、有序字节和blob映射
import {
SyncExpanseStrMap,
SyncExpanseBytesMap,
SyncExpanseOrderedBytesMap,
SyncExpanseBlobMap,
} from '@orieg/expanse';
const names = new SyncExpanseStrMap(); // NUL-free string keys, u64 values
names.set('alice', 1n);
names.update('alice', (cur) => (cur ?? 0n) + 1n); // atomic increment
names.next('alice'); // { key: ..., value: ... } | null, byte-lexicographical order
const raw = new SyncExpanseBytesMap(); // Buffer / Uint8Array / string keys, any bytes
raw.compareExchange(Buffer.from([0, 1]), null, 5n); // { stored: true, observed: null }
const ordered = new SyncExpanseOrderedBytesMap();
ordered.first(); // { key: Buffer, value: bigint } | null
const blobs = new SyncExpanseBlobMap();
blobs.set(1n, Buffer.alloc(64, 1), 5 /* hotMeta */);
blobs.get(1n); // Buffer
blobs.compareExchangeMeta(1n, 5, 6); // { stored: true, observed: 5 }
blobs.getOrInsert(2n, 'payload longer than seven bytes'); // null: stored
四种都是一样的 compareExchange(key, expected, newValue) 和 getOrInsert 形式
SyncExpanseMap (null 缺席; { stored, observed }), update(key, fn) 在三上
word-valued地图。字符串映射拒绝带有嵌入式NUL的键。 get, has,重点
写入和字符串映射 first / last / next / prev 在乐观的道路上奔跑;这个
ordered-bytes地图的 first / last / next / prev 运行与作家排除在外,所以每个调用
阻塞并发写器。Blob读取返回owned Buffer秒; getWithMeta 增加了 isInline,
任何长度的有效负载都保留了编写时使用的热元数据(#1548)。blob compareExchange,
compareExchangeMeta 和 getOrInsert 当map拒绝该值时抛出(元数据超过24位,
(拒绝竞技场),其中C形式返回状态。就像 SyncExpanseMap,对象属于
一个JavaScript线程,因此交换只损失在两者之间进行的写操作(例如在内部) fn)。
许可证
在MIT和Apache-2.0下双重授权。