UNPKG

nanolith

Version:

Multi-threading in no time with seamless TypeScript support.

1 lines 3.48 kB
import{randomUUID as v4}from"crypto";import{createSharedArrayBuffer,encodeValue,isSharedMapRawData}from"./utilities.js";import*as Keys from"./keys.js";import{Bytes,NULL_ENCODED,ENCODER,DECODER,NULL}from"../constants/shared_map.js";import{createMutex,lockMutex,unlockMutex}from"../utilities/index.js";export class SharedMap{#e;#t;#s;#r=v4();#a;get uniqueKey(){return this.#r}get ID(){return this.#a}static option=Bytes;option=Bytes;get raw(){return Object.freeze({__keys:this.#e,__values:this.#t,__identifier:this.#a,__mutex:this.#s})}constructor(e,{bytes:t,multiplier:s=10}={}){if("object"!=typeof e||Array.isArray(e))throw new Error("Can only provide objects to SharedMap.");if(isSharedMapRawData(e))return this.#e=e.__keys,this.#t=e.__values,this.#a=e.__identifier,void(this.#s=e.__mutex);const r=Object.entries(e),{preppedKeys:a,preppedValues:n,totalLength:i}=r.reduce(((e,[t,s])=>{let r=null==s?NULL_ENCODED:encodeValue(ENCODER,s);r.byteLength<=0&&(r=NULL_ENCODED);const a=r.byteLength-1+e.totalLength,n=Keys.createKey({name:t,start:e.totalLength,end:a});return e.preppedKeys.push(n),e.preppedValues.push(r),e.totalLength+=r.byteLength,e}),{preppedKeys:[],preppedValues:[],totalLength:0}),o=ENCODER.encode(a.join(""));if(this.#e=createSharedArrayBuffer(o.byteLength*s||Bytes.kilobyte),this.#e.set(o),t&&t<i)throw new Error(`${t} isn't enough bytes to store all values. Total byteLength of values is ${i}.`);this.#t=createSharedArrayBuffer(t||i*s||3*Bytes.kilobyte),n.reduce(((e,t)=>(this.#t.set(t,e),e+=t.byteLength)),0),this.#a=v4(),this.#s=createMutex()}async#n(e){await lockMutex(this.#s);const t=await e();return unlockMutex(this.#s),t}async*entries(){const e=(await this.#n((()=>DECODER.decode(this.#e)))).match(/[^;]+\(\d+,\d+\);/g)??[];for(const t of e){const e=Keys.parseKey(t);yield[e.name,this.#i(e)]}}#o(e){const t=DECODER.decode(this.#e),s=Keys.matchKey(t,e);return s||null}#i({start:e,end:t}){if(void 0===e||void 0===t)throw new Error("Failed to parse key");const s=this.#t.subarray(e,t+1);if(this.#y(s))return null;return DECODER.decode(this.#t.subarray(e,t+1))}#h(e){const t=this.#o(e);return t?this.#i(Keys.parseKey(t)):null}#y(e){return e.length===NULL_ENCODED.length&&!NULL_ENCODED.some(((t,s)=>t!==e[s]))}get(e){return this.#n((()=>this.#h(e)))}#u(e,t){const s=DECODER.decode(this.#e).replace(/\x00/g,""),r=s.match(/\d+(?=\);($|\x00))/g)?.[0];let a=encodeValue(ENCODER,t);if(a.byteLength<=0&&(a=NULL_ENCODED),!r||!Keys.createKeyRegex(e).test(s)){const t=r?+r+1:0,n=t+a.byteLength-1,i=Keys.createKey({name:e,start:t,end:n});return this.#e.set(ENCODER.encode(s.concat(i))),this.#t.set(a,t),a}const n=Keys.matchKey(s,e);if(!n)throw new Error("Failed to parse keys.");const{start:i,end:o}=Keys.parseKey(n),y=o-i+1;if(y===a.byteLength)return this.#t.set(a,i),a;const h=i+a.byteLength;if(o!==+r){const e=this.#t.subarray(o+1,+r+1);this.#t.set(e,h)}this.#t.set(a,i);const u=s.split(/(?<=;)/g),l=u.findIndex((t=>Keys.createKeyRegex(e).test(t))),c=Keys.createKey({name:e,start:i,end:h-1});u.splice(l,1,c);for(let e=l+1;e<u.length;e++){const{name:t,start:s,end:r}=Keys.parseKey(u[e]),n=r-s+1,i=s-y+a.byteLength,o=i+n-1;u.splice(e,1,Keys.createKey({name:t,start:i,end:o}))}let d=u.join("");return d.length<s.length&&(d+="\0".repeat(s.length-d.length)),this.#e.set(ENCODER.encode(d)),a}set(e,t){return this.#n((async()=>{const s="function"!=typeof t?t:await t(this.#h(e));this.#u(e,s)}))}async delete(e){await this.#n((()=>{if(this.#o(e))return this.#u(e,NULL)}))}}