@bnaya/objectbuffer
Advanced tools
Comparing version 0.0.0-a3f2701 to 0.0.0-ab8ba96
@@ -11,4 +11,3 @@ /** | ||
/** */ | ||
export { createObjectBuffer, resizeObjectBuffer, getUnderlyingArrayBuffer, loadObjectBuffer, replaceUnderlyingArrayBuffer // eslint-disable-next-line @typescript-eslint/camelcase | ||
, sizeOf as unreliable_sizeOf, memoryStats, disposeWrapperObject, updateExternalArgs } from "./internal/api"; | ||
export { createObjectBuffer, resizeObjectBuffer, getUnderlyingArrayBuffer, loadObjectBuffer, replaceUnderlyingArrayBuffer, sizeOf as unreliable_sizeOf, memoryStats, disposeWrapperObject, updateExternalArgs } from "./internal/api"; | ||
export { acquireLock, acquireLockWait, releaseLock } from "./internal/locks"; |
import { IMemPool } from "@thi.ng/malloc"; | ||
export declare function allocationsTransaction(operation: () => void, pool: IMemPool): void; | ||
export declare function allocationsTransaction<T>(operation: () => T, pool: IMemPool): T; | ||
//# sourceMappingURL=allocationsTransaction.d.ts.map |
@@ -58,6 +58,9 @@ import { OutOfMemoryError } from "./exceptions"; // extend pool and not monkey patch? need to think about it | ||
operation(); | ||
pool.malloc = originalMalloc; | ||
pool.calloc = originalCalloc; | ||
pool.realloc = originalRealloc; | ||
try { | ||
return operation(); | ||
} finally { | ||
pool.malloc = originalMalloc; | ||
pool.calloc = originalCalloc; | ||
pool.realloc = originalRealloc; | ||
} | ||
} |
@@ -23,4 +23,4 @@ import { ExternalArgsApi } from "./interfaces"; | ||
*/ | ||
export declare function resizeObjectBuffer(objectBuffer: any, newSize: number): ArrayBuffer; | ||
export declare function getUnderlyingArrayBuffer(objectBuffer: any): ArrayBuffer | SharedArrayBuffer; | ||
export declare function resizeObjectBuffer(objectBuffer: unknown, newSize: number): ArrayBuffer; | ||
export declare function getUnderlyingArrayBuffer(objectBuffer: unknown): ArrayBuffer | SharedArrayBuffer; | ||
/** | ||
@@ -46,3 +46,3 @@ * Create objectBuffer object from the given ArrayBuffer | ||
*/ | ||
export declare function replaceUnderlyingArrayBuffer(objectBuffer: any, newArrayBuffer: ArrayBuffer | SharedArrayBuffer): void; | ||
export declare function replaceUnderlyingArrayBuffer(objectBuffer: unknown, newArrayBuffer: ArrayBuffer | SharedArrayBuffer): void; | ||
export { sizeOf } from "./sizeOf"; | ||
@@ -52,3 +52,3 @@ /** | ||
*/ | ||
export declare function memoryStats(objectBuffer: any): { | ||
export declare function memoryStats(objectBuffer: unknown): { | ||
available: number; | ||
@@ -61,3 +61,3 @@ used: number; | ||
*/ | ||
export declare function updateExternalArgs(objectBuffer: any, options: Partial<ExternalArgsApi>): void; | ||
export declare function updateExternalArgs(objectBuffer: unknown, options: Partial<ExternalArgsApi>): void; | ||
//# sourceMappingURL=api.d.ts.map |
import { initializeArrayBuffer } from "./store"; | ||
import { objectSaver } from "./objectSaver"; | ||
import { createObjectWrapper } from "./objectWrapper"; | ||
import { arrayBufferCopyTo, externalArgsApiToExternalArgsApi, getInternalAPI, isPrimitive } from "./utils"; | ||
import { arrayBufferCopyTo, externalArgsApiToExternalArgsApi, getInternalAPI, isSupportedTopLevelValue } from "./utils"; | ||
import { getCacheFor } from "./externalObjectsCache"; | ||
@@ -9,2 +8,5 @@ import { INITIAL_ENTRY_POINTER_TO_POINTER, MEM_POOL_START } from "./consts"; | ||
import { UnsupportedOperationError } from "./exceptions"; | ||
import { createHeap } from "../structsGenerator/consts"; | ||
import { saveValueIterative } from "./saveValue"; | ||
import { allocationsTransaction } from "./allocationsTransaction"; | ||
@@ -17,3 +19,3 @@ /** | ||
export function createObjectBuffer(externalArgs, size, initialValue, options = {}) { | ||
if (Array.isArray(initialValue) || initialValue instanceof Date || initialValue instanceof Map || initialValue instanceof Set || isPrimitive(initialValue)) { | ||
if (!isSupportedTopLevelValue(initialValue)) { | ||
throw new UnsupportedOperationError(); | ||
@@ -30,13 +32,9 @@ } | ||
const carrier = { | ||
// dataView, | ||
allocator, | ||
uint8: new Uint8Array(arrayBuffer), | ||
uint16: new Uint16Array(arrayBuffer), | ||
uint32: new Uint32Array(arrayBuffer), | ||
float64: new Float64Array(arrayBuffer), | ||
bigUint64: new BigUint64Array(arrayBuffer) | ||
heap: createHeap(arrayBuffer) | ||
}; | ||
const start = objectSaver(externalArgsApiToExternalArgsApi(externalArgs), carrier, [], initialValue); | ||
carrier.uint32[INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT] = start; | ||
return createObjectWrapper(externalArgsApiToExternalArgsApi(externalArgs), carrier, start); | ||
allocationsTransaction(() => { | ||
saveValueIterative(externalArgsApiToExternalArgsApi(externalArgs), carrier, [], INITIAL_ENTRY_POINTER_TO_POINTER, initialValue); | ||
}, allocator); | ||
return createObjectWrapper(externalArgsApiToExternalArgsApi(externalArgs), carrier, carrier.heap.Uint32Array[INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT]); | ||
} | ||
@@ -58,3 +56,3 @@ /** | ||
export function getUnderlyingArrayBuffer(objectBuffer) { | ||
return getInternalAPI(objectBuffer).getCarrier().uint8.buffer; | ||
return getInternalAPI(objectBuffer).getCarrier().heap.Uint8Array.buffer; | ||
} | ||
@@ -72,3 +70,2 @@ /** | ||
export function loadObjectBuffer(externalArgs, arrayBuffer) { | ||
// const dataView = new DataView(arrayBuffer); | ||
const allocator = new MemPool({ | ||
@@ -81,11 +78,6 @@ align: 8, | ||
const carrier = { | ||
// dataView, | ||
allocator, | ||
uint8: new Uint8Array(arrayBuffer), | ||
uint16: new Uint16Array(arrayBuffer), | ||
uint32: new Uint32Array(arrayBuffer), | ||
float64: new Float64Array(arrayBuffer), | ||
bigUint64: new BigUint64Array(arrayBuffer) | ||
heap: createHeap(arrayBuffer) | ||
}; | ||
return createObjectWrapper(externalArgsApiToExternalArgsApi(externalArgs), carrier, carrier.uint32[INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT]); | ||
return createObjectWrapper(externalArgsApiToExternalArgsApi(externalArgs), carrier, carrier.heap.Uint32Array[INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT]); | ||
} | ||
@@ -120,11 +112,6 @@ /** | ||
const carrier = { | ||
// dataView: new DataView(newArrayBuffer), | ||
allocator, | ||
uint8: new Uint8Array(newArrayBuffer), | ||
uint16: new Uint16Array(newArrayBuffer), | ||
uint32: new Uint32Array(newArrayBuffer), | ||
float64: new Float64Array(newArrayBuffer), | ||
bigUint64: new BigUint64Array(newArrayBuffer) | ||
}; // eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
heap: createHeap(newArrayBuffer) | ||
}; // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
@@ -131,0 +118,0 @@ allocator.end = newArrayBuffer.byteLength; |
@@ -1,3 +0,3 @@ | ||
import { ArrayEntry, ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
export declare function arrayGetMetadata(carrier: GlobalCarrier, pointerToArrayEntry: number): ArrayEntry; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { Heap } from "../structsGenerator/consts"; | ||
export declare function arrayGetPointersToValueInIndex(carrier: GlobalCarrier, pointerToArrayEntry: number, indexToGet: number): { | ||
@@ -17,5 +17,5 @@ pointer: number; | ||
*/ | ||
export declare function shrinkArray(externalArgs: ExternalArgs, carrier: GlobalCarrier, pointerToArrayEntry: number, wishedLength: number): void; | ||
export declare function arraySort(externalArgs: ExternalArgs, globalCarrier: GlobalCarrier, pointerToArrayEntry: number, sortComparator?: (a: any, b: any) => 1 | -1 | 0): void; | ||
export declare function arrayReverse(externalArgs: ExternalArgs, carrier: GlobalCarrier, pointerToArrayEntry: number): void; | ||
export declare function shrinkArray(heap: Heap, pointerToArrayEntry: number, wishedLength: number): void; | ||
export declare function arraySort(externalArgs: ExternalArgs, carrier: GlobalCarrier, pointerToArrayEntry: number, sortComparator?: (a: any, b: any) => 1 | -1 | 0): void; | ||
export declare function arrayReverse(carrier: GlobalCarrier, pointerToArrayEntry: number): void; | ||
//# sourceMappingURL=arrayHelpers.d.ts.map |
@@ -1,18 +0,15 @@ | ||
import { readEntry, writeEntry, writeValueInPtrToPtrAndHandleMemory } from "./store"; | ||
import { writeValueInPtrToPtrAndHandleMemory } from "./store"; | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { assertNonNull } from "./assertNonNull"; | ||
export function arrayGetMetadata(carrier, pointerToArrayEntry) { | ||
const arrayEntry = readEntry(carrier, pointerToArrayEntry); | ||
return arrayEntry; | ||
} | ||
import { array_length_get, array_dataspacePointer_get, array_allocatedLength_get, array_length_set, array_set_all, array_refsCount_get } from "./generatedStructs"; | ||
export function arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, indexToGet) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); // out of bound | ||
if (indexToGet >= metadata.length) { | ||
// out of bound | ||
if (indexToGet >= array_length_get(carrier.heap, pointerToArrayEntry)) { | ||
return undefined; | ||
} | ||
const pointerToThePointer = metadata.value + indexToGet * Uint32Array.BYTES_PER_ELEMENT; | ||
const pointer = carrier.uint32[pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
const pointerToThePointer = array_dataspacePointer_get(carrier.heap, pointerToArrayEntry) + indexToGet * Uint32Array.BYTES_PER_ELEMENT; | ||
const pointer = carrier.heap.Uint32Array[pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT]; // @todo avoid intermediate object | ||
return { | ||
@@ -35,3 +32,3 @@ pointer, | ||
assertNonNull(pointers); | ||
carrier.uint32[pointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT] = pointerToEntry; | ||
carrier.heap.Uint32Array[pointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT] = pointerToEntry; | ||
} | ||
@@ -48,16 +45,8 @@ export function setValueAtArrayIndex(externalArgs, carrier, pointerToArrayEntry, indexToSet, value) { | ||
export function extendArrayIfNeeded(externalArgs, carrier, pointerToArrayEntry, wishedLength) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
if (wishedLength > metadata.length) { | ||
if (wishedLength > metadata.allocatedLength) { | ||
reallocateArray(externalArgs, carrier, pointerToArrayEntry, wishedLength + externalArgs.arrayAdditionalAllocation, wishedLength); | ||
if (wishedLength > array_length_get(carrier.heap, pointerToArrayEntry)) { | ||
if (wishedLength > array_allocatedLength_get(carrier.heap, pointerToArrayEntry)) { | ||
reallocateArray(carrier, pointerToArrayEntry, wishedLength + externalArgs.arrayAdditionalAllocation, wishedLength); | ||
} else { | ||
// no need to re-allocated, just push the length forward | ||
writeEntry(carrier, pointerToArrayEntry, { | ||
type: ENTRY_TYPE.ARRAY, | ||
refsCount: metadata.refsCount, | ||
value: metadata.value, | ||
allocatedLength: metadata.allocatedLength, | ||
length: wishedLength | ||
}); | ||
array_length_set(carrier.heap, pointerToArrayEntry, wishedLength); | ||
} | ||
@@ -70,42 +59,18 @@ } | ||
export function shrinkArray(externalArgs, carrier, pointerToArrayEntry, wishedLength) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
writeEntry(carrier, pointerToArrayEntry, { | ||
type: ENTRY_TYPE.ARRAY, | ||
refsCount: metadata.refsCount, | ||
value: metadata.value, | ||
allocatedLength: metadata.allocatedLength, | ||
length: wishedLength | ||
}); | ||
export function shrinkArray(heap, pointerToArrayEntry, wishedLength) { | ||
array_length_set(heap, pointerToArrayEntry, wishedLength); | ||
} | ||
function reallocateArray(externalArgs, carrier, pointerToArrayEntry, newAllocatedLength, newLength) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
const newArrayValueLocation = carrier.allocator.realloc(metadata.value, newAllocatedLength * Uint32Array.BYTES_PER_ELEMENT); // for ( | ||
// let memoryToCopyIndex = 0; | ||
// memoryToCopyIndex < metadata.length; | ||
// memoryToCopyIndex += 1 | ||
// ) { | ||
// carrier.dataView.setUint32( | ||
// newArrayValueLocation + memoryToCopyIndex * Uint32Array.BYTES_PER_ELEMENT, | ||
// carrier.dataView.getUint32( | ||
// metadata.value + memoryToCopyIndex * Uint32Array.BYTES_PER_ELEMENT | ||
// ) | ||
// ); | ||
// } | ||
function reallocateArray(carrier, pointerToArrayEntry, newAllocatedLength, newLength) { | ||
const dataspacePointer = array_dataspacePointer_get(carrier.heap, pointerToArrayEntry); // if array was zero length, dataspacePointer was zero | ||
writeEntry(carrier, pointerToArrayEntry, { | ||
type: ENTRY_TYPE.ARRAY, | ||
refsCount: metadata.refsCount, | ||
value: newArrayValueLocation, | ||
allocatedLength: newAllocatedLength, | ||
length: newLength | ||
}); | ||
const newArrayValueLocation = dataspacePointer !== 0 ? carrier.allocator.realloc(dataspacePointer, newAllocatedLength * Uint32Array.BYTES_PER_ELEMENT) : carrier.allocator.calloc(newAllocatedLength * Uint32Array.BYTES_PER_ELEMENT); | ||
array_set_all(carrier.heap, pointerToArrayEntry, ENTRY_TYPE.ARRAY, array_refsCount_get(carrier.heap, pointerToArrayEntry), newArrayValueLocation, newLength, newAllocatedLength); | ||
} | ||
export function arraySort(externalArgs, globalCarrier, pointerToArrayEntry, sortComparator = defaultCompareFunction) { | ||
const metadata = arrayGetMetadata(globalCarrier, pointerToArrayEntry); | ||
const pointersToValues = [...new Array(metadata.length).keys()].map(index => metadata.value + index * Uint32Array.BYTES_PER_ELEMENT).map(pointerToPointer => globalCarrier.uint32[pointerToPointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
export function arraySort(externalArgs, carrier, pointerToArrayEntry, sortComparator = defaultCompareFunction) { | ||
const arrayDataSpace = array_dataspacePointer_get(carrier.heap, pointerToArrayEntry); | ||
const pointersToValues = [...new Array(array_length_get(carrier.heap, pointerToArrayEntry)).keys()].map(index => arrayDataSpace + index * Uint32Array.BYTES_PER_ELEMENT).map(pointerToPointer => carrier.heap.Uint32Array[pointerToPointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
const sortMe = pointersToValues.map(pointer => { | ||
return [pointer, entryToFinalJavaScriptValue(externalArgs, globalCarrier, pointer)]; | ||
return [pointer, entryToFinalJavaScriptValue(externalArgs, carrier, pointer)]; | ||
}); | ||
@@ -117,3 +82,3 @@ sortMe.sort((a, b) => { | ||
for (let i = 0; i < sortMe.length; i += 1) { | ||
globalCarrier.uint32[(metadata.value + i * Uint32Array.BYTES_PER_ELEMENT) / Uint32Array.BYTES_PER_ELEMENT] = sortMe[i][0]; | ||
carrier.heap.Uint32Array[(arrayDataSpace + i * Uint32Array.BYTES_PER_ELEMENT) / Uint32Array.BYTES_PER_ELEMENT] = sortMe[i][0]; | ||
} | ||
@@ -145,8 +110,6 @@ } // https://stackoverflow.com/a/47349064/711152 | ||
export function arrayReverse(externalArgs, carrier, pointerToArrayEntry) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
export function arrayReverse(carrier, pointerToArrayEntry) { | ||
for (let i = 0; i < Math.floor(array_length_get(carrier.heap, pointerToArrayEntry) / 2); i += 1) { | ||
const theOtherIndex = array_length_get(carrier.heap, pointerToArrayEntry) - i - 1; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
for (let i = 0; i < Math.floor(metadata.length / 2); i += 1) { | ||
const theOtherIndex = metadata.length - i - 1; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const a = arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, i); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
@@ -153,0 +116,0 @@ |
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
export declare function arraySplice(externalArgs: ExternalArgs, globalCarrier: GlobalCarrier, pointerToArrayEntry: number, startArg: number, deleteCountArg?: number, ...itemsToAddArg: Array<any>): any[]; | ||
/** | ||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice#Syntax | ||
this function is not OOM same yet unfortunately, | ||
There's allocations after destructive mutations | ||
*/ | ||
export declare function arraySplice(externalArgs: ExternalArgs, carrier: GlobalCarrier, pointerToArrayEntry: number, startArg: number, deleteCountArg?: number, ...itemsToAddArg: Array<any>): any[]; | ||
//# sourceMappingURL=arraySplice.d.ts.map |
@@ -1,17 +0,25 @@ | ||
import { arrayGetMetadata, getFinalValueAtArrayIndex, shrinkArray, extendArrayIfNeeded, arrayGetPointersToValueInIndex, setValuePointerAtArrayIndex } from "./arrayHelpers"; | ||
import { getFinalValueAtArrayIndex, shrinkArray, extendArrayIfNeeded, arrayGetPointersToValueInIndex, setValuePointerAtArrayIndex } from "./arrayHelpers"; | ||
import { assertNonNull } from "./assertNonNull"; | ||
import { writeValueInPtrToPtr } from "./store"; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice#Syntax | ||
import { writeValueInPtrToPtr, handleArcForDeletedValuePointer } from "./store"; | ||
import { array_length_get } from "./generatedStructs"; | ||
/** | ||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice#Syntax | ||
this function is not OOM same yet unfortunately, | ||
There's allocations after destructive mutations | ||
*/ | ||
export function arraySplice(externalArgs, globalCarrier, pointerToArrayEntry, startArg, deleteCountArg, ...itemsToAddArg) { | ||
const metadata = arrayGetMetadata(globalCarrier, pointerToArrayEntry); | ||
const calcedStart = calculateSpliceStart(metadata.length, startArg); | ||
const calcedDeleteCount = calculateDeleteCount(metadata.length, calcedStart, deleteCountArg); | ||
const newLength = metadata.length + itemsToAddArg.length - calcedDeleteCount; | ||
extendArrayIfNeeded(externalArgs, globalCarrier, pointerToArrayEntry, newLength); | ||
export function arraySplice(externalArgs, carrier, pointerToArrayEntry, startArg, deleteCountArg, ...itemsToAddArg) { | ||
const arrayLength = array_length_get(carrier.heap, pointerToArrayEntry); | ||
const calcedStart = calculateSpliceStart(arrayLength, startArg); | ||
const calcedDeleteCount = calculateDeleteCount(arrayLength, calcedStart, deleteCountArg); | ||
const newLength = arrayLength + itemsToAddArg.length - calcedDeleteCount; | ||
extendArrayIfNeeded(externalArgs, carrier, pointerToArrayEntry, newLength); | ||
const deletedItemsToReturn = []; // can be negative | ||
const itemCountChange = newLength - metadata.length; | ||
const itemCountChange = newLength - arrayLength; | ||
for (let deletedItemIndexToSave = calcedStart; deletedItemIndexToSave < calcedStart + calcedDeleteCount; deletedItemIndexToSave += 1) { | ||
deletedItemsToReturn.push(getFinalValueAtArrayIndex(externalArgs, globalCarrier, pointerToArrayEntry, deletedItemIndexToSave)); | ||
deletedItemsToReturn.push(getFinalValueAtArrayIndex(externalArgs, carrier, pointerToArrayEntry, deletedItemIndexToSave)); | ||
handleArcForDeletedValuePointer(carrier, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, deletedItemIndexToSave).pointer); | ||
} // copy-up items | ||
@@ -28,6 +36,6 @@ | ||
for (let writeValueToIndex = newLength - 1; writeValueToIndex >= calcedStart + itemCountChange; writeValueToIndex -= 1) { | ||
const valueToCopyPointers = arrayGetPointersToValueInIndex(globalCarrier, pointerToArrayEntry, writeValueToIndex - itemCountChange); | ||
const valueToCopyPointers = arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, writeValueToIndex - itemCountChange); | ||
assertNonNull(valueToCopyPointers); | ||
setValuePointerAtArrayIndex(globalCarrier, pointerToArrayEntry, writeValueToIndex, valueToCopyPointers.pointer); | ||
globalCarrier.uint32[valueToCopyPointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT] = 0; | ||
setValuePointerAtArrayIndex(carrier, pointerToArrayEntry, writeValueToIndex, valueToCopyPointers.pointer); | ||
carrier.heap.Uint32Array[valueToCopyPointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT] = 0; | ||
} | ||
@@ -42,17 +50,6 @@ } // copy-down items | ||
else if (itemCountChange < 0) { | ||
for (let writeValueToIndex = calcedStart + itemsToAddArg.length; writeValueToIndex < metadata.length + itemCountChange; writeValueToIndex += 1) { | ||
const valueToCopyPointers = arrayGetPointersToValueInIndex(globalCarrier, pointerToArrayEntry, writeValueToIndex - itemCountChange); | ||
assertNonNull(valueToCopyPointers); | ||
setValuePointerAtArrayIndex(globalCarrier, pointerToArrayEntry, writeValueToIndex, valueToCopyPointers.pointer); // empty old array index, its still allocated! | ||
globalCarrier.uint32[valueToCopyPointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT] = 0; // using that is wastefull | ||
// setValueAtArrayIndex( | ||
// dataView, | ||
// textDecoder, | ||
// textEncoder, | ||
// arrayAdditionalAllocation, | ||
// pointerToArrayEntry, | ||
// writeValueToIndex + calcedDeleteCount, | ||
// undefined | ||
// ); | ||
for (let writeValueToIndex = calcedStart + itemsToAddArg.length; writeValueToIndex < arrayLength + itemCountChange; writeValueToIndex += 1) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const valueToCopyPointers = arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, writeValueToIndex - itemCountChange); | ||
setValuePointerAtArrayIndex(carrier, pointerToArrayEntry, writeValueToIndex, valueToCopyPointers.pointer); | ||
} | ||
@@ -62,9 +59,9 @@ } | ||
for (let i = 0; i < itemsToAddArg.length; i += 1) { | ||
const valueToSetPointers = arrayGetPointersToValueInIndex(globalCarrier, pointerToArrayEntry, calcedStart + i); | ||
const valueToSetPointers = arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, calcedStart + i); | ||
assertNonNull(valueToSetPointers); | ||
writeValueInPtrToPtr(externalArgs, globalCarrier, valueToSetPointers.pointerToThePointer, itemsToAddArg[i]); | ||
writeValueInPtrToPtr(externalArgs, carrier, valueToSetPointers.pointerToThePointer, itemsToAddArg[i]); | ||
} | ||
if (newLength < metadata.length) { | ||
shrinkArray(externalArgs, globalCarrier, pointerToArrayEntry, newLength); | ||
if (newLength < arrayLength) { | ||
shrinkArray(carrier.heap, pointerToArrayEntry, newLength); | ||
} | ||
@@ -81,3 +78,3 @@ | ||
if (deleteCountArg === undefined || deleteCountArg >= arrayLength - start) { | ||
return arrayLength - start; | ||
return Math.min(arrayLength, arrayLength - start); | ||
} | ||
@@ -89,3 +86,3 @@ | ||
return deleteCountArg; | ||
return Math.min(arrayLength, deleteCountArg); | ||
} |
@@ -1,9 +0,9 @@ | ||
import { ExternalArgs, GlobalCarrier, ArrayEntry } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
export declare class ArrayWrapper extends BaseProxyTrap<ArrayEntry> implements ProxyHandler<{}> { | ||
get(target: {}, p: PropertyKey): any; | ||
deleteProperty(target: {}, p: PropertyKey): boolean; | ||
export declare class ArrayWrapper extends BaseProxyTrap implements ProxyHandler<Record<string, unknown>> { | ||
get(target: Record<string, unknown>, p: PropertyKey): any; | ||
deleteProperty(target: Record<string, unknown>, p: PropertyKey): boolean; | ||
enumerate(): PropertyKey[]; | ||
ownKeys(): PropertyKey[]; | ||
getOwnPropertyDescriptor(target: {}, prop: any): { | ||
getOwnPropertyDescriptor(target: Record<string, unknown>, prop: any): { | ||
configurable: boolean; | ||
@@ -17,4 +17,4 @@ enumerable: boolean; | ||
} | undefined; | ||
has(target: {}, p: PropertyKey): boolean; | ||
set(target: {}, accessedProp: PropertyKey, value: any): boolean; | ||
has(target: Record<string, unknown>, p: PropertyKey): boolean; | ||
set(target: Record<string, unknown>, accessedProp: PropertyKey, value: any): boolean; | ||
entries(): Iterable<[number, any]>; | ||
@@ -21,0 +21,0 @@ keys(): Iterable<number>; |
@@ -1,2 +0,2 @@ | ||
import { getFinalValueAtArrayIndex, arrayGetMetadata, setValueAtArrayIndex, arraySort, extendArrayIfNeeded, arrayReverse } from "./arrayHelpers"; | ||
import { getFinalValueAtArrayIndex, setValueAtArrayIndex, arraySort, extendArrayIfNeeded, arrayReverse } from "./arrayHelpers"; | ||
import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
@@ -7,2 +7,3 @@ import { arraySplice } from "./arraySplice"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
import { array_length_get } from "./generatedStructs"; | ||
export class ArrayWrapper extends BaseProxyTrap { | ||
@@ -15,4 +16,4 @@ get(target, p) { | ||
if (p in this && p !== "constructor") { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
return this[p]; | ||
@@ -22,3 +23,3 @@ } | ||
if (p === "length") { | ||
return arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
return array_length_get(this.carrier.heap, this.entryPointer); | ||
} | ||
@@ -32,4 +33,4 @@ | ||
} | ||
} // eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
} // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
@@ -50,3 +51,3 @@ | ||
ownKeys() { | ||
const length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
const length = array_length_get(this.carrier.heap, this.entryPointer); | ||
return [...new Array(length).keys(), "length"]; | ||
@@ -79,3 +80,3 @@ } | ||
const length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
const length = array_length_get(this.carrier.heap, this.entryPointer); | ||
@@ -101,3 +102,3 @@ if (typeof p === "number") { | ||
const currentLength = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
const currentLength = array_length_get(this.carrier.heap, this.entryPointer); | ||
@@ -139,3 +140,3 @@ if (currentLength === value) { | ||
index += 1; | ||
length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
length = array_length_get(this.carrier.heap, this.entryPointer); | ||
} while (index < length); | ||
@@ -151,3 +152,3 @@ } | ||
index += 1; | ||
length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
length = array_length_get(this.carrier.heap, this.entryPointer); | ||
} while (index < length); | ||
@@ -163,3 +164,3 @@ } | ||
index += 1; | ||
length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
length = array_length_get(this.carrier.heap, this.entryPointer); | ||
} while (index < length); | ||
@@ -177,7 +178,9 @@ } | ||
splice(start, deleteCount, ...items) { | ||
return arraySplice(this.externalArgs, this.carrier, this.entryPointer, start, deleteCount, ...items); | ||
return allocationsTransaction(() => { | ||
return arraySplice(this.externalArgs, this.carrier, this.entryPointer, start, deleteCount, ...items); | ||
}, this.carrier.allocator); | ||
} | ||
reverse() { | ||
arrayReverse(this.externalArgs, this.carrier, this.entryPointer); | ||
arrayReverse(this.carrier, this.entryPointer); | ||
return this; | ||
@@ -196,8 +199,5 @@ } // no copy inside array is needed, so we can live with the built-in impl | ||
this.splice(0, 0, ...elements); | ||
return arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
} // public getDataView() { | ||
// return this.carrier.dataView; | ||
// } | ||
return array_length_get(this.carrier.heap, this.entryPointer); | ||
} | ||
getEntryPointer() { | ||
@@ -233,3 +233,5 @@ return this.entryPointer; | ||
export function createArrayWrapper(externalArgs, globalCarrier, entryPointer) { | ||
return new Proxy([], new ArrayWrapper(externalArgs, globalCarrier, entryPointer)); | ||
return new Proxy( // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
[], new ArrayWrapper(externalArgs, globalCarrier, entryPointer)); | ||
} |
@@ -1,3 +0,3 @@ | ||
import { ExternalArgs, GlobalCarrier, InternalAPI, DateEntry, ArrayEntry, ObjectEntry, MapEntry, SetEntry } from "./interfaces"; | ||
export declare class BaseProxyTrap<T extends ObjectEntry | DateEntry | ArrayEntry | MapEntry | SetEntry> implements InternalAPI { | ||
import { ExternalArgs, GlobalCarrier, InternalAPI } from "./interfaces"; | ||
export declare abstract class BaseProxyTrap implements InternalAPI { | ||
protected externalArgs: ExternalArgs; | ||
@@ -15,8 +15,5 @@ protected carrier: GlobalCarrier; | ||
arrayAdditionalAllocation: number; | ||
textDecoder: import("./textEncoderDecoderTypes").TextDecoder; | ||
textEncoder: import("./textEncoderDecoderTypes").TextEncoder; | ||
}>; | ||
protected get entryPointer(): number; | ||
protected get entry(): T; | ||
} | ||
//# sourceMappingURL=BaseProxyTrap.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { incrementRefCount, decrementRefCount, readEntry } from "./store"; | ||
import { incrementRefCount, decrementRefCount } from "./store"; | ||
import { WrapperDestroyed } from "./exceptions"; | ||
@@ -8,7 +8,7 @@ export class BaseProxyTrap { | ||
this._entryPointer = _entryPointer; | ||
incrementRefCount(this.externalArgs, this.carrier, this.entryPointer); | ||
incrementRefCount(this.carrier.heap, this.entryPointer); | ||
} | ||
destroy() { | ||
const newRefCount = decrementRefCount(this.externalArgs, this.carrier, this.entryPointer); | ||
const newRefCount = decrementRefCount(this.carrier.heap, this.entryPointer); | ||
this._entryPointer = 0; | ||
@@ -42,6 +42,2 @@ return newRefCount; | ||
get entry() { | ||
return readEntry(this.carrier, this.entryPointer); | ||
} | ||
} |
@@ -9,2 +9,3 @@ export declare const LOCK_OFFSET = 0; | ||
export declare const FALSE_KNOWN_ADDRESS = 3; | ||
export declare const MAX_64_BIG_INT: bigint; | ||
//# sourceMappingURL=consts.d.ts.map |
@@ -8,2 +8,3 @@ export const LOCK_OFFSET = 0; | ||
export const TRUE_KNOWN_ADDRESS = 2; | ||
export const FALSE_KNOWN_ADDRESS = 3; | ||
export const FALSE_KNOWN_ADDRESS = 3; | ||
export const MAX_64_BIG_INT = BigInt("0xFFFFFFFFFFFFFFFF"); |
@@ -1,4 +0,4 @@ | ||
import { ExternalArgs, GlobalCarrier, DateEntry } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
export declare class DateWrapper extends BaseProxyTrap<DateEntry> implements ProxyHandler<Date> { | ||
export declare class DateWrapper extends BaseProxyTrap implements ProxyHandler<Date> { | ||
private dateObjectForReuse; | ||
@@ -5,0 +5,0 @@ private useMeToGiveNamesToFunctionsAndCacheThem; |
@@ -1,2 +0,1 @@ | ||
import { readEntry, writeEntry } from "./store"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
@@ -6,2 +5,3 @@ import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
import { date_set_all, date_refsCount_get, date_timestamp_get } from "./generatedStructs"; | ||
const getFunctions = ["toString", "toDateString", "toTimeString", "toISOString", "toUTCString", // "toGMTString", | ||
@@ -50,12 +50,8 @@ "getDate", "getDay", "getFullYear", "getHours", "getMilliseconds", "getMinutes", "getMonth", "getSeconds", "getTime", "getTimezoneOffset", "getUTCDate", "getUTCDay", "getUTCFullYear", "getUTCHours", "getUTCMilliseconds", "getUTCMinutes", "getUTCMonth", "getUTCSeconds", // "getYear", | ||
updateDateObjectForReuse() { | ||
const entry = readEntry(this.carrier, this.entryPointer); | ||
this.dateObjectForReuse.setTime(entry.value); | ||
this.dateObjectForReuse.setTime(date_timestamp_get(this.carrier.heap, this.entryPointer)); | ||
} | ||
persistDateObject() { | ||
writeEntry(this.carrier, this.entryPointer, { | ||
type: ENTRY_TYPE.DATE, | ||
refsCount: this.entry.refsCount, | ||
value: this.dateObjectForReuse.getTime() | ||
}); | ||
date_set_all(this.carrier.heap, this.entryPointer, ENTRY_TYPE.DATE, date_refsCount_get(this.carrier.heap, this.entryPointer), // padding | ||
0, this.dateObjectForReuse.getTime()); | ||
} | ||
@@ -62,0 +58,0 @@ |
@@ -17,10 +17,14 @@ import { getInternalAPI } from "./utils"; | ||
if (newRefCount === 0) { | ||
const addressesToFree = getAllLinkedAddresses(internalApi.getCarrier(), false, entryPointer); | ||
const addressesToFree = getAllLinkedAddresses(internalApi.getCarrier().heap, false, entryPointer); | ||
const { | ||
allocator, | ||
heap | ||
} = internalApi.getCarrier(); | ||
for (const address of addressesToFree.leafAddresses) { | ||
internalApi.getCarrier().allocator.free(address); | ||
allocator.free(address); | ||
} | ||
for (const address of addressesToFree.arcAddresses) { | ||
decrementRefCount(internalApi.getExternalArgs(), internalApi.getCarrier(), address); | ||
decrementRefCount(heap, address); | ||
} | ||
@@ -27,0 +31,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { ENTRY_TYPE, isPrimitiveEntryType } from "./entry-types"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { createObjectWrapper } from "./objectWrapper"; | ||
@@ -6,9 +6,9 @@ import { createArrayWrapper } from "./arrayWrapper"; | ||
import { getCacheFor } from "./externalObjectsCache"; | ||
import { decrementRefCount, readEntry } from "./store"; | ||
import { decrementRefCount } from "./store"; | ||
import { getAllLinkedAddresses } from "./getAllLinkedAddresses"; | ||
import { createMapWrapper } from "./mapWrapper"; | ||
import { createSetWrapper } from "./setWrapper"; | ||
import { UNDEFINED_KNOWN_ADDRESS, NULL_KNOWN_ADDRESS, TRUE_KNOWN_ADDRESS, FALSE_KNOWN_ADDRESS } from "./consts"; // declare const FinalizationGroup: any; | ||
// declare const WeakRef: any; | ||
import { UNDEFINED_KNOWN_ADDRESS, NULL_KNOWN_ADDRESS, TRUE_KNOWN_ADDRESS, FALSE_KNOWN_ADDRESS } from "./consts"; | ||
import { typeOnly_type_get, number_value_get, bigint_value_get } from "./generatedStructs"; | ||
import { readString } from "./readString"; | ||
const TYPE_TO_FACTORY = { | ||
@@ -38,32 +38,48 @@ [ENTRY_TYPE.OBJECT]: createObjectWrapper, | ||
const valueEntry = readEntry(carrier, pointerToEntry); | ||
const entryType = typeOnly_type_get(carrier.heap, pointerToEntry); | ||
if (isPrimitiveEntryType(valueEntry.type)) { | ||
return valueEntry.value; | ||
} | ||
switch (entryType) { | ||
case ENTRY_TYPE.NUMBER: | ||
return number_value_get(carrier.heap, pointerToEntry); | ||
break; | ||
if (valueEntry.type === ENTRY_TYPE.OBJECT || valueEntry.type === ENTRY_TYPE.DATE || valueEntry.type === ENTRY_TYPE.ARRAY || valueEntry.type === ENTRY_TYPE.MAP || valueEntry.type === ENTRY_TYPE.SET) { | ||
const cache = getCacheFor(carrier, key => { | ||
finalizer(key, carrier, externalArgs); | ||
}); | ||
let ret = cache.get(pointerToEntry); | ||
case ENTRY_TYPE.STRING: | ||
return readString(carrier.heap, pointerToEntry); | ||
break; | ||
if (!ret) { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
ret = TYPE_TO_FACTORY[valueEntry.type](externalArgs, carrier, pointerToEntry); | ||
cache.set(pointerToEntry, ret); | ||
} | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
return bigint_value_get(carrier.heap, pointerToEntry); | ||
break; | ||
return ret; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
return bigint_value_get(carrier.heap, pointerToEntry) * BigInt("-1"); | ||
break; | ||
} // this is an invariant | ||
if (!(entryType === ENTRY_TYPE.OBJECT || entryType === ENTRY_TYPE.DATE || entryType === ENTRY_TYPE.ARRAY || entryType === ENTRY_TYPE.MAP || entryType === ENTRY_TYPE.SET)) { | ||
throw new Error("Nope Nope Nope"); | ||
} | ||
throw new Error("unsupported yet"); | ||
const cache = getCacheFor(carrier, key => { | ||
finalizer(key, carrier); | ||
}); | ||
let ret = cache.get(pointerToEntry); | ||
if (!ret) { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
ret = TYPE_TO_FACTORY[entryType](externalArgs, carrier, pointerToEntry); | ||
cache.set(pointerToEntry, ret); | ||
} | ||
return ret; | ||
} | ||
/* istanbul ignore next */ | ||
function finalizer(memoryAddress, carrier, externalArgs) { | ||
const newRefsCount = decrementRefCount(externalArgs, carrier, memoryAddress); | ||
function finalizer(memoryAddress, carrier) { | ||
const newRefsCount = decrementRefCount(carrier.heap, memoryAddress); | ||
if (newRefsCount === 0) { | ||
const freeUs = getAllLinkedAddresses(carrier, false, memoryAddress); | ||
const freeUs = getAllLinkedAddresses(carrier.heap, false, memoryAddress); | ||
@@ -75,5 +91,5 @@ for (const address of freeUs.leafAddresses) { | ||
for (const address of freeUs.arcAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
decrementRefCount(carrier.heap, address); | ||
} | ||
} | ||
} |
@@ -1,4 +0,6 @@ | ||
import { WeakValueMap } from "./WeakValueMap"; | ||
import { WeakValueMap } from "./WeakValueMap"; // eslint-disable-next-line @typescript-eslint/ban-types | ||
const externalObjectsCache = new WeakMap(); | ||
export function getCacheFor(obj, externalFinalizer) { | ||
export function getCacheFor( // eslint-disable-next-line @typescript-eslint/ban-types | ||
obj, externalFinalizer) { | ||
let map = externalObjectsCache.get(obj); | ||
@@ -5,0 +7,0 @@ |
@@ -1,7 +0,7 @@ | ||
import { GlobalCarrier } from "./interfaces"; | ||
export declare function getAllLinkedAddresses(carrier: GlobalCarrier, ignoreRefCount: boolean, entryPointer: number): { | ||
import { Heap } from "../structsGenerator/consts"; | ||
export declare function getAllLinkedAddresses(heap: Heap, ignoreRefCount: boolean, entryPointer: number): { | ||
leafAddresses: Set<number>; | ||
arcAddresses: Set<number>; | ||
}; | ||
export declare function getObjectOrMapOrSetAddresses(carrier: GlobalCarrier, internalHashmapPointer: number, leafAddresses: Set<number>, addressesToProcessQueue: number[]): void; | ||
export declare function getObjectOrMapOrSetAddresses(heap: Heap, internalHashmapPointer: number, leafAddresses: Set<number>, addressesToProcessQueue: number[]): void; | ||
//# sourceMappingURL=getAllLinkedAddresses.d.ts.map |
@@ -1,6 +0,6 @@ | ||
import { readEntry } from "./store"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { hashMapGetPointersToFree } from "./hashmap/hashmap"; | ||
import { isKnownAddressValuePointer } from "./utils"; | ||
export function getAllLinkedAddresses(carrier, ignoreRefCount, entryPointer) { | ||
import { typeOnly_type_get, string_charsPointer_get, typeAndRc_refsCount_get, object_pointerToHashMap_get, array_dataspacePointer_get, array_length_get } from "./generatedStructs"; | ||
export function getAllLinkedAddresses(heap, ignoreRefCount, entryPointer) { | ||
const leafAddresses = new Set(); | ||
@@ -17,4 +17,5 @@ const arcAddresses = new Set(); | ||
getAllLinkedAddressesStep(carrier, ignoreRefCount, addressToProcess, leafAddresses, arcAddresses, addressesToProcessQueue); // diffs.push(addressesToProcessQueue.filter((p) => !before.includes(p))); | ||
getAllLinkedAddressesStep(heap, ignoreRefCount, addressToProcess, leafAddresses, arcAddresses, addressesToProcessQueue); // diffs.push(addressesToProcessQueue.filter((p) => !before.includes(p))); | ||
} // console.log(diffs); | ||
// @todo avoid intermediate object | ||
@@ -28,3 +29,3 @@ | ||
function getAllLinkedAddressesStep(carrier, ignoreRefCount, entryPointer, leafAddresses, arcAddresses, addressesToProcessQueue) { | ||
function getAllLinkedAddressesStep(heap, ignoreRefCount, entryPointer, leafAddresses, arcAddresses, addressesToProcessQueue) { | ||
if (isKnownAddressValuePointer(entryPointer) || leafAddresses.has(entryPointer) || arcAddresses.has(entryPointer)) { | ||
@@ -34,7 +35,8 @@ return; | ||
const entry = readEntry(carrier, entryPointer); | ||
const entryType = typeOnly_type_get(heap, entryPointer); // to be used ONLY if the type has ref counter | ||
switch (entry.type) { | ||
const refsCount = typeAndRc_refsCount_get(heap, entryPointer); | ||
switch (entryType) { | ||
case ENTRY_TYPE.NUMBER: | ||
case ENTRY_TYPE.STRING: | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
@@ -45,8 +47,13 @@ case ENTRY_TYPE.BIGINT_POSITIVE: | ||
case ENTRY_TYPE.STRING: | ||
leafAddresses.add(string_charsPointer_get(heap, entryPointer)); | ||
leafAddresses.add(entryPointer); | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.MAP: | ||
case ENTRY_TYPE.SET: | ||
if (entry.refsCount <= 1 || ignoreRefCount) { | ||
if (refsCount <= 1 || ignoreRefCount) { | ||
leafAddresses.add(entryPointer); | ||
getObjectOrMapOrSetAddresses(carrier, entry.value, leafAddresses, addressesToProcessQueue); | ||
getObjectOrMapOrSetAddresses(heap, object_pointerToHashMap_get(heap, entryPointer), leafAddresses, addressesToProcessQueue); | ||
} else { | ||
@@ -59,8 +66,9 @@ arcAddresses.add(entryPointer); | ||
case ENTRY_TYPE.ARRAY: | ||
if (entry.refsCount <= 1 || ignoreRefCount) { | ||
if (refsCount <= 1 || ignoreRefCount) { | ||
leafAddresses.add(entryPointer); | ||
leafAddresses.add(entry.value); | ||
leafAddresses.add(array_dataspacePointer_get(heap, entryPointer)); | ||
const arrayLength = array_length_get(heap, entryPointer); | ||
for (let i = 0; i < entry.allocatedLength; i += 1) { | ||
const valuePointer = carrier.uint32[(entry.value + i * Uint32Array.BYTES_PER_ELEMENT) / Uint32Array.BYTES_PER_ELEMENT]; | ||
for (let i = 0; i < arrayLength; i += 1) { | ||
const valuePointer = heap.Uint32Array[(array_dataspacePointer_get(heap, entryPointer) + i * Uint32Array.BYTES_PER_ELEMENT) / Uint32Array.BYTES_PER_ELEMENT]; | ||
addressesToProcessQueue.push(valuePointer); | ||
@@ -75,3 +83,3 @@ } | ||
case ENTRY_TYPE.DATE: | ||
if (entry.refsCount <= 1 || ignoreRefCount) { | ||
if (refsCount <= 1 || ignoreRefCount) { | ||
leafAddresses.add(entryPointer); | ||
@@ -85,13 +93,11 @@ } else { | ||
default: | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
throw new Error(ENTRY_TYPE[entryType] + " Not implemented yet"); | ||
} | ||
} | ||
export function getObjectOrMapOrSetAddresses(carrier, internalHashmapPointer, leafAddresses, addressesToProcessQueue) { | ||
export function getObjectOrMapOrSetAddresses(heap, internalHashmapPointer, leafAddresses, addressesToProcessQueue) { | ||
const { | ||
pointersToValuePointers, | ||
pointers | ||
} = hashMapGetPointersToFree(carrier, internalHashmapPointer); | ||
} = hashMapGetPointersToFree(heap, internalHashmapPointer); | ||
@@ -103,4 +109,4 @@ for (const leafPointer of pointers) { | ||
for (const pointer of pointersToValuePointers) { | ||
addressesToProcessQueue.push(carrier.uint32[pointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
addressesToProcessQueue.push(heap.Uint32Array[pointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
} | ||
} |
import { GlobalCarrier, ExternalArgs } from "../interfaces"; | ||
import { Heap } from "../../structsGenerator/consts"; | ||
export declare function createHashMap(carrier: GlobalCarrier, | ||
@@ -14,4 +15,4 @@ /** | ||
*/ | ||
export declare function hashMapNodeLookup(carrier: GlobalCarrier, mapPointer: number, externalKeyValue: number | string): number; | ||
export declare function hashMapValueLookup(carrier: GlobalCarrier, mapPointer: number, externalKeyValue: number | string): number; | ||
export declare function hashMapNodeLookup(heap: Heap, mapPointer: number, externalKeyValue: number | string): number; | ||
export declare function hashMapValueLookup(heap: Heap, mapPointer: number, externalKeyValue: number | string): number; | ||
/** | ||
@@ -25,13 +26,13 @@ * @returns the value pointer of the deleted key | ||
*/ | ||
export declare function hashMapLowLevelIterator(carrier: GlobalCarrier, mapPointer: number, nodePointerIteratorToken: number): number; | ||
export declare function hashMapNodePointerToKeyValue(carrier: GlobalCarrier, nodePointer: number): { | ||
export declare function hashMapLowLevelIterator(heap: Heap, mapPointer: number, nodePointerIteratorToken: number): number; | ||
export declare function hashMapNodePointerToKeyValue(heap: Heap, nodePointer: number): { | ||
valuePointer: number; | ||
keyPointer: number; | ||
}; | ||
export declare function hashMapSize(carrier: GlobalCarrier, mapPointer: number): number; | ||
export declare function hashMapGetPointersToFree(carrier: GlobalCarrier, hashmapPointer: number): { | ||
export declare function hashMapSize(heap: Heap, mapPointer: number): number; | ||
export declare function hashMapGetPointersToFree(heap: Heap, hashmapPointer: number): { | ||
pointers: number[]; | ||
pointersToValuePointers: number[]; | ||
}; | ||
export declare function hashmapNodesPointerIterator(carrier: GlobalCarrier, mapPointer: number): Generator<number, void, unknown>; | ||
export declare function hashmapNodesPointerIterator(heap: Heap, mapPointer: number): Generator<number, void, unknown>; | ||
//# sourceMappingURL=hashmap.d.ts.map |
@@ -1,7 +0,8 @@ | ||
import { MAP_MACHINE, NODE_MACHINE } from "./memoryLayout"; | ||
import { hashCodeInPlace, hashCodeExternalValue, getKeyStartLength } from "./hashmapUtils"; | ||
import { primitiveValueToEntry } from "../utils"; | ||
import { sizeOfEntry, writeEntry, readEntry, compareStringOrNumberEntriesInPlace } from "../store"; | ||
import { hashCodeInPlace, hashCodeExternalValue, getKeyStart, getKeyLength } from "./hashmapUtils"; | ||
import { strByteLength } from "../utils"; | ||
import { stringEncodeInto } from "../stringEncodeInto"; | ||
import { compareStringOrNumberEntriesInPlace, readNumberOrString } from "../store"; | ||
import { ENTRY_TYPE } from "../entry-types"; | ||
import { initLinkedList, linkedListItemInsert, linkedListItemRemove, linkedListLowLevelIterator, linkedListGetValue, linkedListGetPointersToFree } from "../linkedList/linkedList"; | ||
import { number_size, string_size, number_set_all, string_set_all, number_value_place, number_value_ctor, typeOnly_type_get, string_charsPointer_get, hashmap_set_all, hashmap_CAPACITY_get, hashmap_ARRAY_POINTER_get, hashmap_USED_CAPACITY_set, hashmap_USED_CAPACITY_get, hashmapNode_KEY_POINTER_get, hashmapNode_NEXT_NODE_POINTER_place, hashmapNode_NEXT_NODE_POINTER_get, hashmapNode_VALUE_POINTER_place, hashmap_LINKED_LIST_POINTER_get, hashmap_LINKED_LIST_SIZE_set, hashmap_LINKED_LIST_SIZE_get, hashmapNode_set_all, hashmap_ARRAY_POINTER_set, hashmap_CAPACITY_set, hashmapNode_NEXT_NODE_POINTER_set, hashmap_size, hashmapNode_size, hashmapNode_LINKED_LIST_ITEM_POINTER_get } from "../generatedStructs"; | ||
export function createHashMap(carrier, | ||
@@ -12,11 +13,10 @@ /** | ||
initialCapacity = 10) { | ||
const hashMapMemory = carrier.allocator.calloc(MAP_MACHINE.map.SIZE_OF); | ||
const arrayMemory = carrier.allocator.calloc(initialCapacity * Uint32Array.BYTES_PER_ELEMENT); | ||
const { | ||
heap, | ||
allocator | ||
} = carrier; | ||
const hashMapMemory = allocator.calloc(hashmap_size); | ||
const arrayMemory = allocator.calloc(initialCapacity * Uint32Array.BYTES_PER_ELEMENT); | ||
const linkedListPointer = initLinkedList(carrier); | ||
const mapMachine = MAP_MACHINE.createOperator(carrier, hashMapMemory); | ||
mapMachine.set("ARRAY_POINTER", arrayMemory); | ||
mapMachine.set("CAPACITY", initialCapacity); | ||
mapMachine.set("USED_CAPACITY", 0); | ||
mapMachine.set("LINKED_LIST_POINTER", linkedListPointer); | ||
mapMachine.set("LINKED_LIST_SIZE", 0); | ||
hashmap_set_all(heap, hashMapMemory, arrayMemory, linkedListPointer, 0, initialCapacity, 0); | ||
return hashMapMemory; | ||
@@ -29,49 +29,64 @@ } | ||
export function hashMapInsertUpdate(externalArgs, carrier, mapPointer, externalKeyValue) { | ||
const mapOperator = MAP_MACHINE.createOperator(carrier, mapPointer); | ||
const keyEntry = primitiveValueToEntry(externalKeyValue); // allocate all possible needed memory upfront, so we won't oom in the middle of something | ||
const { | ||
heap, | ||
allocator | ||
} = carrier; // allocate all possible needed memory upfront, so we won't oom in the middle of something | ||
// in case of overwrite, we will not need this memory | ||
const memoryForNewNode = carrier.allocator.calloc(NODE_MACHINE.map.SIZE_OF); | ||
const memorySizeOfKey = sizeOfEntry(keyEntry); | ||
const keyEntryMemory = carrier.allocator.calloc(memorySizeOfKey); | ||
writeEntry(carrier, keyEntryMemory, keyEntry); | ||
const keyHeaderOverhead = keyEntry.type === ENTRY_TYPE.STRING ? // type + string length | ||
8 + 4 : // type | ||
8; | ||
const keyHashCode = hashCodeInPlace(carrier.uint8, mapOperator.get("CAPACITY"), // + 1 for the type of key | ||
keyEntryMemory + keyHeaderOverhead, memorySizeOfKey - keyHeaderOverhead); | ||
let ptrToPtrToSaveTheNodeTo = mapOperator.get("ARRAY_POINTER") + keyHashCode * Uint32Array.BYTES_PER_ELEMENT; | ||
const commonNodeOperator = NODE_MACHINE.createOperator(carrier, carrier.uint32[ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT]); // todo: share code with hashMapNodeLookup? | ||
const memoryForNewNode = allocator.calloc(hashmapNode_size); | ||
let keyMemoryEntryPointer; | ||
let keyDataMemoryStart; | ||
let keyDataMemoryLength; | ||
while (commonNodeOperator.startAddress !== 0 && !compareStringOrNumberEntriesInPlace(carrier, commonNodeOperator.get("KEY_POINTER"), keyEntryMemory)) { | ||
ptrToPtrToSaveTheNodeTo = commonNodeOperator.pointerTo("NEXT_NODE_POINTER"); | ||
commonNodeOperator.startAddress = commonNodeOperator.get("NEXT_NODE_POINTER"); | ||
} // first item added to bucket | ||
if (typeof externalKeyValue === "number") { | ||
keyMemoryEntryPointer = allocator.calloc(number_size); | ||
number_set_all(carrier.heap, keyMemoryEntryPointer, ENTRY_TYPE.NUMBER, externalKeyValue); | ||
keyDataMemoryStart = keyMemoryEntryPointer + number_value_place; | ||
keyDataMemoryLength = number_value_ctor.BYTES_PER_ELEMENT; | ||
} else { | ||
keyMemoryEntryPointer = allocator.calloc(string_size); | ||
keyDataMemoryLength = strByteLength(externalKeyValue); | ||
keyDataMemoryStart = allocator.calloc(keyDataMemoryLength); | ||
stringEncodeInto(carrier.heap.Uint8Array, keyDataMemoryStart, externalKeyValue); | ||
string_set_all(carrier.heap, keyMemoryEntryPointer, ENTRY_TYPE.STRING, keyDataMemoryLength, keyDataMemoryStart); | ||
} | ||
const keyHashCode = hashCodeInPlace(heap.Uint8Array, hashmap_CAPACITY_get(heap, mapPointer), keyDataMemoryStart, keyDataMemoryLength); | ||
const bucketStartPointer = hashmap_ARRAY_POINTER_get(heap, mapPointer) + keyHashCode * Uint32Array.BYTES_PER_ELEMENT; | ||
let ptrToPtrToSaveTheNodeTo = bucketStartPointer; | ||
let iteratedNodePointer = heap.Uint32Array[ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT]; // todo: share code with hashMapNodeLookup? | ||
if (ptrToPtrToSaveTheNodeTo === mapOperator.get("ARRAY_POINTER") + keyHashCode * Uint32Array.BYTES_PER_ELEMENT) { | ||
mapOperator.set("USED_CAPACITY", mapOperator.get("USED_CAPACITY") + 1); | ||
while (iteratedNodePointer !== 0 && !compareStringOrNumberEntriesInPlace(carrier.heap, hashmapNode_KEY_POINTER_get(heap, iteratedNodePointer), keyMemoryEntryPointer)) { | ||
ptrToPtrToSaveTheNodeTo = iteratedNodePointer + hashmapNode_NEXT_NODE_POINTER_place; | ||
iteratedNodePointer = hashmapNode_NEXT_NODE_POINTER_get(heap, iteratedNodePointer); | ||
} // bucket was empty, first item added to bucket | ||
if (ptrToPtrToSaveTheNodeTo === bucketStartPointer) { | ||
hashmap_USED_CAPACITY_set(heap, mapPointer, hashmap_USED_CAPACITY_get(heap, mapPointer) + 1); | ||
} // found node with same key, return same pointer | ||
if (commonNodeOperator.startAddress !== 0) { | ||
if (iteratedNodePointer !== 0) { | ||
// we don't need the new memory | ||
carrier.allocator.free(keyEntryMemory); | ||
carrier.allocator.free(memoryForNewNode); | ||
return commonNodeOperator.pointerTo("VALUE_POINTER"); | ||
// @todo Free here also the string data | ||
if (typeOnly_type_get(carrier.heap, keyMemoryEntryPointer) === ENTRY_TYPE.STRING) { | ||
allocator.free(string_charsPointer_get(carrier.heap, keyMemoryEntryPointer)); | ||
} // we don't need to new memory | ||
allocator.free(keyMemoryEntryPointer); | ||
allocator.free(memoryForNewNode); | ||
return iteratedNodePointer + hashmapNode_VALUE_POINTER_place; | ||
} else { | ||
commonNodeOperator.startAddress = memoryForNewNode; | ||
commonNodeOperator.set("KEY_POINTER", keyEntryMemory); | ||
commonNodeOperator.set("LINKED_LIST_ITEM_POINTER", linkedListItemInsert(carrier, mapOperator.get("LINKED_LIST_POINTER"), memoryForNewNode)); | ||
carrier.uint32[ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT] = memoryForNewNode; | ||
mapOperator.set("LINKED_LIST_SIZE", mapOperator.get("LINKED_LIST_SIZE") + 1); | ||
iteratedNodePointer = memoryForNewNode; | ||
hashmapNode_set_all(heap, iteratedNodePointer, 0, 0, keyMemoryEntryPointer, linkedListItemInsert(carrier, hashmap_LINKED_LIST_POINTER_get(heap, mapPointer), memoryForNewNode)); | ||
heap.Uint32Array[ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT] = memoryForNewNode; | ||
hashmap_LINKED_LIST_SIZE_set(heap, mapPointer, hashmap_LINKED_LIST_SIZE_get(heap, mapPointer) + 1); | ||
if (shouldRehash(mapOperator.get("LINKED_LIST_SIZE"), mapOperator.get("CAPACITY"), mapOperator.get("USED_CAPACITY"), externalArgs.hashMapLoadFactor)) { | ||
// console.log("rehash", { | ||
// USED_CAPACITY: mapOperator.get("USED_CAPACITY") | ||
// }); | ||
hashMapRehash(carrier, mapOperator, mapOperator.get("CAPACITY") * 2); | ||
if (shouldRehash(hashmap_CAPACITY_get(heap, mapPointer), hashmap_USED_CAPACITY_get(heap, mapPointer), externalArgs.hashMapLoadFactor)) { | ||
hashMapRehash(carrier, mapPointer, hashmap_CAPACITY_get(heap, mapPointer) * 2); | ||
} | ||
return commonNodeOperator.pointerTo("VALUE_POINTER"); | ||
return iteratedNodePointer + hashmapNode_VALUE_POINTER_place; | ||
} | ||
@@ -83,17 +98,17 @@ } | ||
export function hashMapNodeLookup(carrier, mapPointer, externalKeyValue) { | ||
const mapMachine = MAP_MACHINE.createOperator(carrier, mapPointer); | ||
const keyHashCode = hashCodeExternalValue(mapMachine.get("CAPACITY"), externalKeyValue); | ||
let ptrToPtr = mapMachine.get("ARRAY_POINTER") + keyHashCode * Uint32Array.BYTES_PER_ELEMENT; | ||
const node = NODE_MACHINE.createOperator(carrier, carrier.uint32[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT]); | ||
export function hashMapNodeLookup(heap, mapPointer, externalKeyValue) { | ||
const keyHashCode = hashCodeExternalValue(hashmap_CAPACITY_get(heap, mapPointer), externalKeyValue); | ||
const bucketStartPtrToPtr = hashmap_ARRAY_POINTER_get(heap, mapPointer) + keyHashCode * Uint32Array.BYTES_PER_ELEMENT; | ||
let ptrToPtr = bucketStartPtrToPtr; | ||
let iteratedNode = heap.Uint32Array[bucketStartPtrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
while (node.startAddress !== 0) { | ||
const keyEntry = readEntry(carrier, node.get("KEY_POINTER")); | ||
while (iteratedNode !== 0) { | ||
const keyValue = readNumberOrString(heap, hashmapNode_KEY_POINTER_get(heap, iteratedNode)); | ||
if (keyEntry.value === externalKeyValue) { | ||
if (keyValue === externalKeyValue) { | ||
return ptrToPtr; | ||
} | ||
ptrToPtr = node.pointerTo("NEXT_NODE_POINTER"); | ||
node.startAddress = node.get("NEXT_NODE_POINTER"); | ||
ptrToPtr = iteratedNode + hashmapNode_NEXT_NODE_POINTER_place; | ||
iteratedNode = hashmapNode_NEXT_NODE_POINTER_get(heap, iteratedNode); | ||
} | ||
@@ -103,4 +118,4 @@ | ||
} | ||
export function hashMapValueLookup(carrier, mapPointer, externalKeyValue) { | ||
const nodePtrToPtr = hashMapNodeLookup(carrier, mapPointer, externalKeyValue); | ||
export function hashMapValueLookup(heap, mapPointer, externalKeyValue) { | ||
const nodePtrToPtr = hashMapNodeLookup(heap, mapPointer, externalKeyValue); | ||
@@ -111,4 +126,3 @@ if (nodePtrToPtr === 0) { | ||
const node = NODE_MACHINE.createOperator(carrier, carrier.uint32[nodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT]); | ||
return node.pointerTo("VALUE_POINTER"); | ||
return heap.Uint32Array[nodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT] + hashmapNode_VALUE_POINTER_place; | ||
} | ||
@@ -120,3 +134,7 @@ /** | ||
export function hashMapDelete(carrier, mapPointer, externalKeyValue) { | ||
const foundNodePtrToPtr = hashMapNodeLookup(carrier, mapPointer, externalKeyValue); | ||
const { | ||
heap, | ||
allocator | ||
} = carrier; | ||
const foundNodePtrToPtr = hashMapNodeLookup(heap, mapPointer, externalKeyValue); | ||
@@ -127,11 +145,15 @@ if (foundNodePtrToPtr === 0) { | ||
const nodeToDeletePointer = carrier.uint32[foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
const nodeOperator = NODE_MACHINE.createOperator(carrier, nodeToDeletePointer); | ||
const valuePointer = nodeOperator.pointerTo("VALUE_POINTER"); | ||
linkedListItemRemove(carrier, nodeOperator.get("LINKED_LIST_ITEM_POINTER")); // remove node from bucket | ||
const nodeToDeletePointer = heap.Uint32Array[foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
const valuePointer = nodeToDeletePointer + hashmapNode_VALUE_POINTER_place; | ||
linkedListItemRemove(carrier, hashmapNode_LINKED_LIST_ITEM_POINTER_get(heap, nodeToDeletePointer)); // remove node from bucket | ||
carrier.uint32[foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT] = nodeOperator.get("NEXT_NODE_POINTER"); | ||
carrier.allocator.free(nodeOperator.get("KEY_POINTER")); | ||
carrier.allocator.free(nodeOperator.startAddress); | ||
carrier.uint32[(mapPointer + MAP_MACHINE.map.LINKED_LIST_SIZE.bytesOffset) / Uint32Array.BYTES_PER_ELEMENT]--; | ||
heap.Uint32Array[foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT] = hashmapNode_NEXT_NODE_POINTER_get(heap, nodeToDeletePointer); | ||
if (typeOnly_type_get(heap, hashmapNode_KEY_POINTER_get(heap, nodeToDeletePointer)) === ENTRY_TYPE.STRING) { | ||
allocator.free(string_charsPointer_get(heap, hashmapNode_KEY_POINTER_get(heap, nodeToDeletePointer))); | ||
} | ||
allocator.free(hashmapNode_KEY_POINTER_get(heap, nodeToDeletePointer)); | ||
allocator.free(nodeToDeletePointer); | ||
hashmap_LINKED_LIST_SIZE_set(heap, mapPointer, hashmap_LINKED_LIST_SIZE_get(heap, mapPointer) - 1); | ||
return valuePointer; | ||
@@ -144,11 +166,10 @@ } | ||
export function hashMapLowLevelIterator(carrier, mapPointer, nodePointerIteratorToken) { | ||
const mapOperator = MAP_MACHINE.createOperator(carrier, mapPointer); | ||
export function hashMapLowLevelIterator(heap, mapPointer, nodePointerIteratorToken) { | ||
let tokenToUseForLinkedListIterator = 0; | ||
if (nodePointerIteratorToken !== 0) { | ||
tokenToUseForLinkedListIterator = NODE_MACHINE.createOperator(carrier, nodePointerIteratorToken).get("LINKED_LIST_ITEM_POINTER"); | ||
tokenToUseForLinkedListIterator = hashmapNode_LINKED_LIST_ITEM_POINTER_get(heap, nodePointerIteratorToken); | ||
} | ||
const pointerToNextLinkedListItem = linkedListLowLevelIterator(carrier, mapOperator.get("LINKED_LIST_POINTER"), tokenToUseForLinkedListIterator); | ||
const pointerToNextLinkedListItem = linkedListLowLevelIterator(heap, hashmap_LINKED_LIST_POINTER_get(heap, mapPointer), tokenToUseForLinkedListIterator); | ||
@@ -159,28 +180,31 @@ if (pointerToNextLinkedListItem === 0) { | ||
return linkedListGetValue(carrier, pointerToNextLinkedListItem); | ||
return linkedListGetValue(heap, pointerToNextLinkedListItem); | ||
} | ||
export function hashMapNodePointerToKeyValue(carrier, nodePointer) { | ||
const operator = NODE_MACHINE.createOperator(carrier, nodePointer); | ||
export function hashMapNodePointerToKeyValue(heap, nodePointer) { | ||
// @todo avoid intermediate object | ||
return { | ||
valuePointer: operator.pointerTo("VALUE_POINTER"), | ||
keyPointer: operator.get("KEY_POINTER") | ||
valuePointer: nodePointer + hashmapNode_VALUE_POINTER_place, | ||
keyPointer: hashmapNode_KEY_POINTER_get(heap, nodePointer) | ||
}; | ||
} | ||
export function hashMapSize(carrier, mapPointer) { | ||
return carrier.uint32[(mapPointer + MAP_MACHINE.map.LINKED_LIST_SIZE.bytesOffset) / Uint32Array.BYTES_PER_ELEMENT]; | ||
export function hashMapSize(heap, mapPointer) { | ||
return hashmap_LINKED_LIST_SIZE_get(heap, mapPointer); | ||
} | ||
export function hashMapGetPointersToFree(carrier, hashmapPointer) { | ||
const mapOperator = MAP_MACHINE.createOperator(carrier, hashmapPointer); | ||
const pointers = [hashmapPointer, mapOperator.get("ARRAY_POINTER")]; | ||
export function hashMapGetPointersToFree(heap, hashmapPointer) { | ||
const pointers = [hashmapPointer, hashmap_ARRAY_POINTER_get(heap, hashmapPointer)]; | ||
const pointersToValuePointers = []; | ||
const pointersOfLinkedList = linkedListGetPointersToFree(carrier, mapOperator.get("LINKED_LIST_POINTER")); | ||
const pointersOfLinkedList = linkedListGetPointersToFree(heap, hashmap_LINKED_LIST_POINTER_get(heap, hashmapPointer)); | ||
pointers.push(...pointersOfLinkedList.pointers); | ||
const nodeOperator = NODE_MACHINE.createOperator(carrier, 0); | ||
for (const nodePointer of pointersOfLinkedList.valuePointers) { | ||
nodeOperator.startAddress = nodePointer; | ||
pointersToValuePointers.push(nodeOperator.pointerTo("VALUE_POINTER")); | ||
pointers.push(nodePointer, nodeOperator.get("KEY_POINTER")); | ||
} | ||
pointersToValuePointers.push(nodePointer + hashmapNode_VALUE_POINTER_place); | ||
if (typeOnly_type_get(heap, hashmapNode_KEY_POINTER_get(heap, nodePointer)) === ENTRY_TYPE.STRING) { | ||
pointers.push(string_charsPointer_get(heap, hashmapNode_KEY_POINTER_get(heap, nodePointer))); | ||
} | ||
pointers.push(nodePointer, hashmapNode_KEY_POINTER_get(heap, nodePointer)); | ||
} // @todo avoid intermediate object | ||
return { | ||
@@ -192,50 +216,29 @@ pointers, | ||
function hashMapRehash(carrier, mapOperator, newCapacity) { | ||
// const before = { | ||
// ARRAY_POINTER: mapOperator.get("ARRAY_POINTER"), | ||
// CAPACITY: mapOperator.get("CAPACITY"), | ||
// USED_CAPACITY: mapOperator.get("USED_CAPACITY") | ||
// }; | ||
carrier.allocator.free(mapOperator.get("ARRAY_POINTER")); | ||
const biggerArray = carrier.allocator.calloc(newCapacity * Uint32Array.BYTES_PER_ELEMENT); // (carrier.allocator as typeof carrier.allocator & { u8: Uint8Array }).u8.fill( | ||
// 0, | ||
// biggerArray, | ||
// biggerArray + newCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
// ); | ||
function hashMapRehash(carrier, hashmapPointer, newCapacity) { | ||
const { | ||
heap, | ||
allocator | ||
} = carrier; // Why not realloc? | ||
mapOperator.set("ARRAY_POINTER", biggerArray); | ||
mapOperator.set("CAPACITY", newCapacity); // const after = { | ||
// ARRAY_POINTER: mapOperator.get("ARRAY_POINTER"), | ||
// CAPACITY: mapOperator.get("CAPACITY"), | ||
// USED_CAPACITY: mapOperator.get("USED_CAPACITY") | ||
// }; | ||
allocator.free(hashmap_ARRAY_POINTER_get(heap, hashmapPointer)); | ||
const biggerArray = carrier.allocator.calloc(newCapacity * Uint32Array.BYTES_PER_ELEMENT); | ||
hashmap_ARRAY_POINTER_set(heap, hashmapPointer, biggerArray); | ||
hashmap_CAPACITY_set(heap, hashmapPointer, newCapacity); | ||
let pointerToNode = 0; | ||
while ((pointerToNode = hashMapLowLevelIterator(carrier, mapOperator.startAddress, pointerToNode)) !== 0) { | ||
hashMapRehashInsert(carrier, biggerArray, newCapacity, pointerToNode); | ||
while ((pointerToNode = hashMapLowLevelIterator(heap, hashmapPointer, pointerToNode)) !== 0) { | ||
hashMapRehashInsert(heap, biggerArray, newCapacity, pointerToNode); | ||
} | ||
} | ||
function hashMapRehashInsert(carrier, bucketsArrayPointer, arraySize, nodePointer) { | ||
const nodeOperator = NODE_MACHINE.createOperator(carrier, nodePointer); | ||
const keyInfo = getKeyStartLength(carrier, nodeOperator.get("KEY_POINTER")); | ||
const keyHashCode = hashCodeInPlace(carrier.uint8, arraySize, keyInfo.start, keyInfo.length); | ||
function hashMapRehashInsert(heap, bucketsArrayPointer, arraySize, nodePointer) { | ||
const keyHashCode = hashCodeInPlace(heap.Uint8Array, arraySize, getKeyStart(heap, nodePointer), getKeyLength(heap, nodePointer)); | ||
const bucket = keyHashCode % arraySize; | ||
const bucketStartPointer = bucketsArrayPointer + bucket * Uint32Array.BYTES_PER_ELEMENT; | ||
const prevFirstNodeInBucket = carrier.uint32[bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
carrier.uint32[bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT] = nodePointer; | ||
nodeOperator.set("NEXT_NODE_POINTER", prevFirstNodeInBucket); // // Add is first node in bucket | ||
// if (nodeOperator.startAddress === 0) { | ||
// carrier.dataView.setUint32(bucketStartPointer, nodePointer); | ||
// return; | ||
// } | ||
// // find last node. | ||
// while (nodeOperator.get("NEXT_NODE_POINTER") !== 0) { | ||
// nodeOperator.startAddress = nodeOperator.get("NEXT_NODE_POINTER"); | ||
// } | ||
// nodeOperator.set("NEXT_NODE_POINTER", nodePointer); | ||
const prevFirstNodeInBucket = heap.Uint32Array[bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
heap.Uint32Array[bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT] = nodePointer; | ||
hashmapNode_NEXT_NODE_POINTER_set(heap, nodePointer, prevFirstNodeInBucket); | ||
} | ||
function shouldRehash(nodesCount, buckets, fullBuckets, loadFactor) { | ||
function shouldRehash(buckets, fullBuckets, loadFactor) { | ||
// add proportion check? | ||
@@ -246,8 +249,8 @@ // nodesCount | ||
export function* hashmapNodesPointerIterator(carrier, mapPointer) { | ||
export function* hashmapNodesPointerIterator(heap, mapPointer) { | ||
let iteratorToken = 0; | ||
while ((iteratorToken = hashMapLowLevelIterator(carrier, mapPointer, iteratorToken)) !== 0) { | ||
while ((iteratorToken = hashMapLowLevelIterator(heap, mapPointer, iteratorToken)) !== 0) { | ||
yield iteratorToken; | ||
} | ||
} |
@@ -1,9 +0,6 @@ | ||
import { GlobalCarrier } from "../interfaces"; | ||
import { Heap } from "../../structsGenerator/consts"; | ||
export declare function hashCodeInPlace(uint8: Uint8Array, capacity: number, keyStart: number, keyBytesLength: number): number; | ||
export declare function hashCodeExternalValue(capacity: number, value: string | number): number; | ||
export declare function hashCodeEntry(carrier: GlobalCarrier, capacity: number, pointer: number): number; | ||
export declare function getKeyStartLength(carrier: GlobalCarrier, keyPointer: number): { | ||
start: number; | ||
length: number; | ||
}; | ||
export declare function getKeyStart(heap: Heap, keyPointer: number): number; | ||
export declare function getKeyLength(heap: Heap, keyPointer: number): number; | ||
//# sourceMappingURL=hashmapUtils.d.ts.map |
import { ENTRY_TYPE } from "../entry-types"; | ||
import { stringEncodeInto } from "../stringEncodeInto"; | ||
import { number_value_place, string_charsPointer_get, typeOnly_type_get, number_value_ctor, string_bytesLength_get } from "../generatedStructs"; | ||
export function hashCodeInPlace(uint8, capacity, keyStart, keyBytesLength) { | ||
@@ -26,23 +27,16 @@ let h = 0 | 0; // const hashed: number[] = []; | ||
} | ||
export function hashCodeEntry(carrier, capacity, pointer) { | ||
const type = carrier.uint8[pointer]; | ||
if (type === ENTRY_TYPE.NUMBER) { | ||
return hashCodeInPlace(carrier.uint8, capacity, pointer + 1, 8); | ||
export function getKeyStart(heap, keyPointer) { | ||
if (typeOnly_type_get(heap, keyPointer) === ENTRY_TYPE.NUMBER) { | ||
return keyPointer + number_value_place; | ||
} else { | ||
return hashCodeInPlace(carrier.uint8, capacity, pointer + 1 + Uint16Array.BYTES_PER_ELEMENT, carrier.uint16[(pointer + 1) / Uint16Array.BYTES_PER_ELEMENT]); | ||
// string | ||
return string_charsPointer_get(heap, keyPointer); | ||
} | ||
} | ||
export function getKeyStartLength(carrier, keyPointer) { | ||
if (carrier.uint32[keyPointer / Uint32Array.BYTES_PER_ELEMENT] === ENTRY_TYPE.NUMBER) { | ||
return { | ||
start: keyPointer + 1, | ||
length: Float64Array.BYTES_PER_ELEMENT | ||
}; | ||
export function getKeyLength(heap, keyPointer) { | ||
if (typeOnly_type_get(heap, keyPointer) === ENTRY_TYPE.NUMBER) { | ||
return number_value_ctor.BYTES_PER_ELEMENT; | ||
} else { | ||
return { | ||
start: keyPointer + 1 + 2 + 2, | ||
length: carrier.uint16[(keyPointer + 1) / Uint16Array.BYTES_PER_ELEMENT] | ||
}; | ||
return string_bytesLength_get(heap, keyPointer); | ||
} | ||
} |
@@ -1,68 +0,6 @@ | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { TextDecoder, TextEncoder } from "./textEncoderDecoderTypes"; | ||
export declare type primitive = string | number | bigint | boolean | undefined | null; | ||
export declare type Entry = StringEntry | NumberEntry | BigIntPositiveEntry | BigIntNegativeEntry | ObjectEntry | ArrayEntry | DateEntry | MapEntry | SetEntry; | ||
export interface StringEntry { | ||
type: ENTRY_TYPE.STRING; | ||
value: string; | ||
allocatedBytes: number; | ||
} | ||
export interface NumberEntry { | ||
type: ENTRY_TYPE.NUMBER; | ||
value: number; | ||
} | ||
export interface BigIntPositiveEntry { | ||
type: ENTRY_TYPE.BIGINT_POSITIVE; | ||
value: bigint; | ||
} | ||
export interface BigIntNegativeEntry { | ||
type: ENTRY_TYPE.BIGINT_NEGATIVE; | ||
value: bigint; | ||
} | ||
export interface ObjectEntry { | ||
type: ENTRY_TYPE.OBJECT; | ||
refsCount: number; | ||
/** | ||
* Pointer to hashmap | ||
*/ | ||
value: number; | ||
} | ||
export interface ArrayEntry { | ||
type: ENTRY_TYPE.ARRAY; | ||
refsCount: number; | ||
value: number; | ||
length: number; | ||
allocatedLength: number; | ||
} | ||
export interface DateEntry { | ||
type: ENTRY_TYPE.DATE; | ||
refsCount: number; | ||
value: number; | ||
} | ||
export interface MapEntry { | ||
type: ENTRY_TYPE.MAP; | ||
refsCount: number; | ||
/** | ||
* Pointer to hashmap | ||
*/ | ||
value: number; | ||
} | ||
export interface SetEntry { | ||
type: ENTRY_TYPE.SET; | ||
refsCount: number; | ||
/** | ||
* Pointer to hashmap | ||
*/ | ||
value: number; | ||
} | ||
/** | ||
* The carrier object allows us to swap the DataView easily | ||
*/ | ||
import type { Heap } from "../structsGenerator/consts"; | ||
import type { IMemPool } from "@thi.ng/malloc"; | ||
export interface GlobalCarrier { | ||
uint8: Uint8Array; | ||
uint16: Uint16Array; | ||
uint32: Uint32Array; | ||
float64: Float64Array; | ||
bigUint64: BigUint64Array; | ||
allocator: import("@thi.ng/malloc").IMemPool; | ||
allocator: IMemPool; | ||
heap: Heap; | ||
} | ||
@@ -72,5 +10,7 @@ export declare type ExternalArgs = Readonly<{ | ||
hashMapMinInitialCapacity: number; | ||
/** | ||
* Allocate additional memory for array pointers, | ||
* will prevent the reallocation and copy when array is getting bigger | ||
*/ | ||
arrayAdditionalAllocation: number; | ||
textDecoder: TextDecoder; | ||
textEncoder: TextEncoder; | ||
}>; | ||
@@ -81,4 +21,2 @@ export declare type ExternalArgsApi = Readonly<{ | ||
arrayAdditionalAllocation?: number; | ||
textDecoder: TextDecoder; | ||
textEncoder: TextEncoder; | ||
}>; | ||
@@ -85,0 +23,0 @@ export interface InternalAPI { |
@@ -1,18 +0,9 @@ | ||
import { GlobalCarrier } from "../interfaces"; | ||
export declare const LINKED_LIST_ITEM_MACHINE: { | ||
map: import("../memoryMachinery").MemoryMap<"NEXT_POINTER" | "VALUE">; | ||
createOperator: (carrier: GlobalCarrier, address: number) => import("../memoryMachinery").MemoryOperator<"NEXT_POINTER" | "VALUE">; | ||
}; | ||
export declare type LinkedListItemMachineType = ReturnType<typeof LINKED_LIST_ITEM_MACHINE.createOperator>; | ||
export declare const LINKED_LIST_MACHINE: { | ||
map: import("../memoryMachinery").MemoryMap<"END_POINTER" | "START_POINTER">; | ||
createOperator: (carrier: GlobalCarrier, address: number) => import("../memoryMachinery").MemoryOperator<"END_POINTER" | "START_POINTER">; | ||
}; | ||
export declare type LinkedListMachineType = ReturnType<typeof LINKED_LIST_MACHINE.createOperator>; | ||
import type { GlobalCarrier } from "../interfaces"; | ||
import { Heap } from "../../structsGenerator/consts"; | ||
export declare function initLinkedList(carrier: GlobalCarrier): number; | ||
export declare function linkedListItemInsert(carrier: GlobalCarrier, linkedListPointer: number, nodeValuePointer: number): number; | ||
export declare function linkedListItemRemove(carrier: GlobalCarrier, itemPointer: number): void; | ||
export declare function linkedListLowLevelIterator(carrier: GlobalCarrier, linkedListPointer: number, itemPointer: number): number; | ||
export declare function linkedListGetValue(carrier: GlobalCarrier, itemPointer: number): number; | ||
export declare function linkedListGetPointersToFree(carrier: GlobalCarrier, linkedListPointer: number): { | ||
export declare function linkedListItemRemove({ heap, allocator }: GlobalCarrier, itemPointer: number): void; | ||
export declare function linkedListLowLevelIterator(heap: Heap, linkedListPointer: number, itemPointer: number): number; | ||
export declare function linkedListGetValue(heap: Heap, itemPointer: number): number; | ||
export declare function linkedListGetPointersToFree(heap: Heap, linkedListPointer: number): { | ||
pointers: number[]; | ||
@@ -19,0 +10,0 @@ valuePointers: number[]; |
@@ -1,2 +0,2 @@ | ||
import { createMemoryMachine } from "../memoryMachinery"; | ||
import { linkedListItem_size, linkedList_size, linkedList_set_all, linkedListItem_set_all, linkedList_END_POINTER_get, linkedList_END_POINTER_set, linkedListItem_NEXT_POINTER_get, linkedListItem_VALUE_get, linkedList_START_POINTER_get } from "../generatedStructs"; | ||
@@ -17,50 +17,40 @@ /* | ||
*/ | ||
export const LINKED_LIST_ITEM_MACHINE = createMemoryMachine({ | ||
NEXT_POINTER: Uint32Array, | ||
VALUE: Uint32Array | ||
}); | ||
export const LINKED_LIST_MACHINE = createMemoryMachine({ | ||
END_POINTER: Uint32Array, | ||
START_POINTER: Uint32Array | ||
}); | ||
export function initLinkedList(carrier) { | ||
const { | ||
allocator | ||
allocator, | ||
heap | ||
} = carrier; | ||
const memoryForLinkedList = allocator.calloc(LINKED_LIST_MACHINE.map.SIZE_OF); | ||
const memoryForEndMarkerItem = allocator.calloc(LINKED_LIST_ITEM_MACHINE.map.SIZE_OF); | ||
const linkedListMachine = LINKED_LIST_MACHINE.createOperator(carrier, memoryForLinkedList); | ||
linkedListMachine.set("START_POINTER", memoryForEndMarkerItem); | ||
linkedListMachine.set("END_POINTER", memoryForEndMarkerItem); | ||
const memoryForLinkedList = allocator.calloc(linkedList_size); | ||
const memoryForEndMarkerItem = allocator.calloc(linkedListItem_size); | ||
linkedList_set_all(heap, memoryForLinkedList, memoryForEndMarkerItem, memoryForEndMarkerItem); | ||
return memoryForLinkedList; | ||
} | ||
export function linkedListItemInsert(carrier, linkedListPointer, nodeValuePointer) { | ||
const newItemMemory = carrier.allocator.calloc(LINKED_LIST_ITEM_MACHINE.map.SIZE_OF); | ||
const linkedListOperator = LINKED_LIST_MACHINE.createOperator(carrier, linkedListPointer); | ||
const wasEndMarkerOperator = LINKED_LIST_ITEM_MACHINE.createOperator(carrier, linkedListOperator.get("END_POINTER")); | ||
const toBeEndMarkerOperator = LINKED_LIST_ITEM_MACHINE.createOperator(carrier, newItemMemory); | ||
toBeEndMarkerOperator.set("VALUE", 0); | ||
toBeEndMarkerOperator.set("NEXT_POINTER", 0); | ||
linkedListOperator.set("END_POINTER", toBeEndMarkerOperator.startAddress); | ||
wasEndMarkerOperator.set("VALUE", toBeEndMarkerOperator.startAddress); | ||
wasEndMarkerOperator.set("NEXT_POINTER", toBeEndMarkerOperator.startAddress); | ||
wasEndMarkerOperator.set("VALUE", nodeValuePointer); | ||
return wasEndMarkerOperator.startAddress; | ||
const { | ||
allocator, | ||
heap | ||
} = carrier; | ||
const newEndMarker = allocator.calloc(linkedListItem_size); | ||
linkedListItem_set_all(heap, newEndMarker, 0, 0); | ||
const wasEndMarker = linkedList_END_POINTER_get(heap, linkedListPointer); | ||
linkedList_END_POINTER_set(heap, linkedListPointer, newEndMarker); | ||
linkedListItem_set_all(heap, wasEndMarker, newEndMarker, nodeValuePointer); | ||
return wasEndMarker; | ||
} | ||
export function linkedListItemRemove(carrier, itemPointer) { | ||
const itemToOverwrite = LINKED_LIST_ITEM_MACHINE.createOperator(carrier, itemPointer); | ||
const itemToOverwriteWith = LINKED_LIST_ITEM_MACHINE.createOperator(carrier, itemToOverwrite.get("NEXT_POINTER")); | ||
const memoryToFree = itemToOverwrite.get("NEXT_POINTER"); | ||
itemToOverwrite.set("VALUE", itemToOverwriteWith.get("VALUE")); | ||
itemToOverwrite.set("NEXT_POINTER", itemToOverwriteWith.get("NEXT_POINTER")); | ||
carrier.allocator.free(memoryToFree); | ||
export function linkedListItemRemove({ | ||
heap, | ||
allocator | ||
}, itemPointer) { | ||
const memoryToFree = linkedListItem_NEXT_POINTER_get(heap, itemPointer); | ||
linkedListItem_set_all(heap, itemPointer, linkedListItem_NEXT_POINTER_get(heap, memoryToFree), linkedListItem_VALUE_get(heap, memoryToFree)); | ||
allocator.free(memoryToFree); | ||
} | ||
export function linkedListLowLevelIterator(carrier, linkedListPointer, itemPointer) { | ||
const listItem = LINKED_LIST_ITEM_MACHINE.createOperator(carrier, itemPointer); | ||
export function linkedListLowLevelIterator(heap, linkedListPointer, itemPointer) { | ||
let iteratedItem = itemPointer; // new iteration session | ||
if (itemPointer === 0) { | ||
const list = LINKED_LIST_MACHINE.createOperator(carrier, linkedListPointer); | ||
listItem.startAddress = list.get("START_POINTER"); // can be zero if START_POINTER pointes to the end marker | ||
// read the first item in the list | ||
iteratedItem = linkedList_START_POINTER_get(heap, linkedListPointer); // can be zero if START_POINTER pointes to the end marker | ||
if (listItem.get("VALUE") === 0) { | ||
if (linkedListItem_VALUE_get(heap, iteratedItem) === 0) { | ||
return 0; | ||
@@ -70,27 +60,26 @@ } // can be zero if START_POINTER pointes to the end marker | ||
return listItem.startAddress; | ||
return iteratedItem; | ||
} // deleted during iteration | ||
if (listItem.get("VALUE") === 0) { | ||
if (linkedListItem_VALUE_get(heap, iteratedItem) === 0) { | ||
return 0; | ||
} | ||
listItem.startAddress = listItem.get("NEXT_POINTER"); // next item is the end | ||
iteratedItem = linkedListItem_NEXT_POINTER_get(heap, iteratedItem); // next item is the end | ||
if (listItem.get("VALUE") === 0) { | ||
if (linkedListItem_VALUE_get(heap, iteratedItem) === 0) { | ||
return 0; | ||
} | ||
return listItem.startAddress; | ||
return iteratedItem; | ||
} | ||
export function linkedListGetValue(carrier, itemPointer) { | ||
return LINKED_LIST_ITEM_MACHINE.createOperator(carrier, itemPointer).get("VALUE"); | ||
export function linkedListGetValue(heap, itemPointer) { | ||
return linkedListItem_VALUE_get(heap, itemPointer); | ||
} | ||
export function linkedListGetPointersToFree(carrier, linkedListPointer) { | ||
export function linkedListGetPointersToFree(heap, linkedListPointer) { | ||
const pointers = [linkedListPointer]; | ||
const valuePointers = []; | ||
const operator = LINKED_LIST_MACHINE.createOperator(carrier, linkedListPointer); | ||
const firstItem = operator.get("START_POINTER"); | ||
const lastItem = operator.get("END_POINTER"); // list empty | ||
const firstItem = linkedList_START_POINTER_get(heap, linkedListPointer); | ||
const lastItem = linkedList_END_POINTER_get(heap, linkedListPointer); // list empty | ||
@@ -101,14 +90,15 @@ if (firstItem === lastItem) { | ||
const linkItemOperator = LINKED_LIST_ITEM_MACHINE.createOperator(carrier, linkedListLowLevelIterator(carrier, linkedListPointer, 0)); | ||
let iterator = linkedListLowLevelIterator(heap, linkedListPointer, 0); | ||
while (linkItemOperator.startAddress !== 0) { | ||
pointers.push(linkItemOperator.startAddress); // value = 0 means end marker | ||
while (iterator !== 0) { | ||
pointers.push(iterator); // value = 0 means end marker | ||
if (linkItemOperator.get("VALUE") !== 0) { | ||
valuePointers.push(linkItemOperator.get("VALUE")); // linkItemOperator.startAddress = 0; | ||
if (linkedListItem_VALUE_get(heap, iterator) !== 0) { | ||
valuePointers.push(linkedListItem_VALUE_get(heap, iterator)); | ||
} | ||
linkItemOperator.startAddress = linkItemOperator.get("NEXT_POINTER"); | ||
} | ||
iterator = linkedListItem_NEXT_POINTER_get(heap, iterator); | ||
} // @todo avoid intermediate object | ||
return { | ||
@@ -115,0 +105,0 @@ pointers, |
@@ -0,1 +1,3 @@ | ||
/* istanbul ignore file */ | ||
// I'm not sure how to test that yet | ||
import { invariant } from "./utils"; | ||
@@ -2,0 +4,0 @@ import { getUnderlyingArrayBuffer } from "./api"; |
@@ -1,5 +0,5 @@ | ||
import { ExternalArgs, GlobalCarrier, MapEntry, InternalAPI } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier, InternalAPI } from "./interfaces"; | ||
import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
export declare class MapWrapper<K extends string | number, V> extends BaseProxyTrap<MapEntry> implements Map<K, V> { | ||
export declare class MapWrapper<K extends string | number, V> extends BaseProxyTrap implements Map<K, V> { | ||
clear(): void; | ||
@@ -6,0 +6,0 @@ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void; |
@@ -7,2 +7,3 @@ import { deleteObjectPropertyEntryByKey, objectGet, objectSet, mapOrSetClear } from "./objectWrapperHelpers"; | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { object_pointerToHashMap_get } from "./generatedStructs"; | ||
export class MapWrapper extends BaseProxyTrap { | ||
@@ -20,3 +21,3 @@ clear() { | ||
get size() { | ||
return hashMapSize(this.carrier, this.entry.value); | ||
return hashMapSize(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer)); | ||
} | ||
@@ -29,8 +30,8 @@ | ||
*entries() { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier, this.entry.value)) { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer))) { | ||
const { | ||
valuePointer, | ||
keyPointer | ||
} = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
yield [entryToFinalJavaScriptValue(this.externalArgs, this.carrier, keyPointer), entryToFinalJavaScriptValue(this.externalArgs, this.carrier, this.carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT])]; | ||
} = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
yield [entryToFinalJavaScriptValue(this.externalArgs, this.carrier, keyPointer), entryToFinalJavaScriptValue(this.externalArgs, this.carrier, this.carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT])]; | ||
} | ||
@@ -40,4 +41,4 @@ } | ||
*keys() { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier, this.entry.value)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer))) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
yield entryToFinalJavaScriptValue(this.externalArgs, this.carrier, t.keyPointer); | ||
@@ -48,7 +49,7 @@ } | ||
*values() { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier, this.entry.value)) { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer))) { | ||
const { | ||
valuePointer | ||
} = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
yield entryToFinalJavaScriptValue(this.externalArgs, this.carrier, this.carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
} = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
yield entryToFinalJavaScriptValue(this.externalArgs, this.carrier, this.carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
} | ||
@@ -74,3 +75,3 @@ } | ||
return objectGet(this.externalArgs, this.carrier, this.entry.value, p); | ||
return objectGet(this.externalArgs, this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p); | ||
} | ||
@@ -83,3 +84,3 @@ | ||
return deleteObjectPropertyEntryByKey(this.externalArgs, this.carrier, this.entry.value, p); | ||
return deleteObjectPropertyEntryByKey(this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p); | ||
} | ||
@@ -92,3 +93,3 @@ | ||
return hashMapNodeLookup(this.carrier, this.entry.value, p) !== 0; | ||
return hashMapNodeLookup(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p) !== 0; | ||
} | ||
@@ -102,3 +103,3 @@ | ||
allocationsTransaction(() => { | ||
objectSet(this.externalArgs, this.carrier, this.entry.value, p, value); | ||
objectSet(this.externalArgs, this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p, value); | ||
}, this.carrier.allocator); | ||
@@ -105,0 +106,0 @@ return this; |
@@ -1,14 +0,14 @@ | ||
import { ObjectEntry, ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
export declare class ObjectWrapper extends BaseProxyTrap<ObjectEntry> implements ProxyHandler<{}> { | ||
get(target: {}, p: PropertyKey): any; | ||
deleteProperty(target: {}, p: PropertyKey): boolean; | ||
export declare class ObjectWrapper extends BaseProxyTrap implements ProxyHandler<Record<string, unknown>> { | ||
get(target: Record<string, unknown>, p: PropertyKey): any; | ||
deleteProperty(target: Record<string, unknown>, p: PropertyKey): boolean; | ||
enumerate(): PropertyKey[]; | ||
ownKeys(): PropertyKey[]; | ||
getOwnPropertyDescriptor(target: {}, p: PropertyKey): { | ||
getOwnPropertyDescriptor(target: Record<string, unknown>, p: PropertyKey): { | ||
configurable: boolean; | ||
enumerable: boolean; | ||
} | undefined; | ||
has(target: {}, p: PropertyKey): boolean; | ||
set(target: {}, p: PropertyKey, value: any): boolean; | ||
has(target: Record<string, unknown>, p: PropertyKey): boolean; | ||
set(target: Record<string, unknown>, p: PropertyKey, value: any): boolean; | ||
isExtensible(): boolean; | ||
@@ -15,0 +15,0 @@ preventExtensions(): boolean; |
@@ -7,2 +7,3 @@ import { getObjectPropertiesEntries, deleteObjectPropertyEntryByKey, objectGet, objectSet } from "./objectWrapperHelpers"; | ||
import { hashMapNodeLookup } from "./hashmap/hashmap"; | ||
import { object_pointerToHashMap_get } from "./generatedStructs"; | ||
export class ObjectWrapper extends BaseProxyTrap { | ||
@@ -18,3 +19,3 @@ get(target, p) { | ||
return objectGet(this.externalArgs, this.carrier, this.entry.value, p); | ||
return objectGet(this.externalArgs, this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p); | ||
} | ||
@@ -27,7 +28,7 @@ | ||
return deleteObjectPropertyEntryByKey(this.externalArgs, this.carrier, this.entry.value, p); | ||
return deleteObjectPropertyEntryByKey(this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p); | ||
} | ||
enumerate() { | ||
const gotEntries = getObjectPropertiesEntries(this.carrier, this.entry.value); | ||
const gotEntries = getObjectPropertiesEntries(this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer)); | ||
return gotEntries.map(e => e.key); | ||
@@ -37,3 +38,3 @@ } | ||
ownKeys() { | ||
const gotEntries = getObjectPropertiesEntries(this.carrier, this.entry.value); | ||
const gotEntries = getObjectPropertiesEntries(this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer)); | ||
return gotEntries.map(e => e.key); | ||
@@ -62,3 +63,3 @@ } | ||
return hashMapNodeLookup(this.carrier, this.entry.value, p) !== 0; | ||
return hashMapNodeLookup(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p) !== 0; | ||
} | ||
@@ -72,3 +73,3 @@ | ||
allocationsTransaction(() => { | ||
objectSet(this.externalArgs, this.carrier, this.entry.value, p, value); | ||
objectSet(this.externalArgs, this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p, value); | ||
}, this.carrier.allocator); | ||
@@ -75,0 +76,0 @@ return true; |
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
export declare function deleteObjectPropertyEntryByKey(externalArgs: ExternalArgs, carrier: GlobalCarrier, hashmapPointer: number, keyToDeleteBy: string | number): boolean; | ||
export declare function deleteObjectPropertyEntryByKey(carrier: GlobalCarrier, hashmapPointer: number, keyToDeleteBy: string | number): boolean; | ||
export declare function getObjectPropertiesEntries(carrier: GlobalCarrier, hashmapPointer: number): Array<{ | ||
@@ -7,5 +7,5 @@ key: string | number; | ||
}>; | ||
export declare function objectSet(externalArgs: ExternalArgs, carrier: GlobalCarrier, hashMapPointer: number, p: string | number, value: any): void; | ||
export declare function objectSet(externalArgs: ExternalArgs, carrier: GlobalCarrier, hashMapPointer: number, p: string | number, value: unknown): void; | ||
export declare function objectGet(externalArgs: ExternalArgs, carrier: GlobalCarrier, entryPointer: number, key: string | number): any; | ||
export declare function mapOrSetClear(externalArgs: ExternalArgs, carrier: GlobalCarrier, mapOrSetPtr: number): void; | ||
//# sourceMappingURL=objectWrapperHelpers.d.ts.map |
@@ -1,6 +0,9 @@ | ||
import { readEntry, writeValueInPtrToPtrAndHandleMemory, handleArcForDeletedValuePointer, decrementRefCount, writeEntry, setRefCount } from "./store"; | ||
import { writeValueInPtrToPtrAndHandleMemory, handleArcForDeletedValuePointer, decrementRefCount } from "./store"; | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { hashMapDelete, hashMapLowLevelIterator, hashMapNodePointerToKeyValue, hashMapInsertUpdate, hashMapValueLookup, createHashMap } from "./hashmap/hashmap"; | ||
import { getAllLinkedAddresses } from "./getAllLinkedAddresses"; | ||
export function deleteObjectPropertyEntryByKey(externalArgs, carrier, hashmapPointer, keyToDeleteBy) { | ||
import { typeOnly_type_get, number_value_get, typeAndRc_refsCount_get, typeAndRc_refsCount_set, object_pointerToHashMap_set } from "./generatedStructs"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { readString } from "./readString"; | ||
export function deleteObjectPropertyEntryByKey(carrier, hashmapPointer, keyToDeleteBy) { | ||
const deletedValuePointerToPointer = hashMapDelete(carrier, hashmapPointer, keyToDeleteBy); // no such key | ||
@@ -12,4 +15,4 @@ | ||
const deletedValuePointer = carrier.uint32[deletedValuePointerToPointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
handleArcForDeletedValuePointer(externalArgs, carrier, deletedValuePointer); | ||
const deletedValuePointer = carrier.heap.Uint32Array[deletedValuePointerToPointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
handleArcForDeletedValuePointer(carrier, deletedValuePointer); | ||
return true; | ||
@@ -21,11 +24,12 @@ } | ||
while (iterator = hashMapLowLevelIterator(carrier, hashmapPointer, iterator)) { | ||
while (iterator = hashMapLowLevelIterator(carrier.heap, hashmapPointer, iterator)) { | ||
const { | ||
valuePointer, | ||
keyPointer | ||
} = hashMapNodePointerToKeyValue(carrier, iterator); | ||
const keyEntry = readEntry(carrier, keyPointer); | ||
} = hashMapNodePointerToKeyValue(carrier.heap, iterator); | ||
const typeOfKeyEntry = typeOnly_type_get(carrier.heap, keyPointer); | ||
const key = typeOfKeyEntry === ENTRY_TYPE.NUMBER ? number_value_get(carrier.heap, keyPointer) : readString(carrier.heap, keyPointer); | ||
foundValues.push({ | ||
valuePointer: carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT], | ||
key: keyEntry.value | ||
valuePointer: carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT], | ||
key | ||
}); | ||
@@ -41,9 +45,4 @@ } | ||
export function objectGet(externalArgs, carrier, entryPointer, key) { | ||
const valuePointer = hashMapValueLookup(carrier, entryPointer, key); | ||
if (valuePointer === 0) { | ||
return undefined; | ||
} | ||
return entryToFinalJavaScriptValue(externalArgs, carrier, carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
const valuePointer = hashMapValueLookup(carrier.heap, entryPointer, key); | ||
return entryToFinalJavaScriptValue(externalArgs, carrier, carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT]); | ||
} // export function hashmapClearFree( | ||
@@ -71,9 +70,9 @@ // externalArgs: ExternalArgs, | ||
export function mapOrSetClear(externalArgs, carrier, mapOrSetPtr) { | ||
const entry = readEntry(carrier, mapOrSetPtr); // we fake the entry refCount as zero so getAllLinkedAddresses will visit what's needed | ||
const prevCount = setRefCount(carrier, mapOrSetPtr, 0); | ||
// we fake the entry refCount as zero so getAllLinkedAddresses will visit what's needed | ||
const prevCount = typeAndRc_refsCount_get(carrier.heap, mapOrSetPtr); | ||
typeAndRc_refsCount_set(carrier.heap, mapOrSetPtr, 0); | ||
const { | ||
leafAddresses, | ||
arcAddresses | ||
} = getAllLinkedAddresses(carrier, false, mapOrSetPtr); | ||
} = getAllLinkedAddresses(carrier.heap, false, mapOrSetPtr); | ||
@@ -95,10 +94,8 @@ for (const address of leafAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
} // hashmapClearFree(externalArgs, carrier, entry.value); | ||
// Restore real ref count | ||
decrementRefCount(carrier.heap, address); | ||
} // Restore real ref count | ||
setRefCount(carrier, mapOrSetPtr, prevCount); | ||
entry.value = createHashMap(carrier, externalArgs.hashMapMinInitialCapacity); | ||
writeEntry(carrier, mapOrSetPtr, entry); | ||
typeAndRc_refsCount_set(carrier.heap, mapOrSetPtr, prevCount); | ||
object_pointerToHashMap_set(carrier.heap, mapOrSetPtr, createHashMap(carrier, externalArgs.hashMapMinInitialCapacity)); | ||
} |
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
/** | ||
* Returns pointer for the value | ||
*/ | ||
export declare function saveValue(externalArgs: ExternalArgs, carrier: GlobalCarrier, referencedPointers: number[], value: any): number; | ||
export declare function saveValueIterative(externalArgs: ExternalArgs, carrier: GlobalCarrier, referencedExistingPointers: number[], initialValuePtrToPtr: number, initialValue: unknown): void; | ||
//# sourceMappingURL=saveValue.d.ts.map |
@@ -1,56 +0,121 @@ | ||
import { primitiveValueToEntry, isPrimitive, getOurPointerIfApplicable } from "./utils"; | ||
import { appendEntry } from "./store"; | ||
import { objectSaver, mapSaver, setSaver } from "./objectSaver"; | ||
import { arraySaver } from "./arraySaver"; | ||
import { getOurPointerIfApplicable, strByteLength } from "./utils"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { UNDEFINED_KNOWN_ADDRESS, NULL_KNOWN_ADDRESS, TRUE_KNOWN_ADDRESS, FALSE_KNOWN_ADDRESS } from "./consts"; | ||
/** | ||
* Returns pointer for the value | ||
*/ | ||
import { number_size, number_set_all, bigint_size, bigint_set_all, string_size, string_set_all, date_size, date_set_all } from "./generatedStructs"; | ||
import { UNDEFINED_KNOWN_ADDRESS, NULL_KNOWN_ADDRESS, TRUE_KNOWN_ADDRESS, FALSE_KNOWN_ADDRESS, MAX_64_BIG_INT } from "./consts"; | ||
import { arraySaverIterative } from "./arraySaverIterative"; | ||
import { objectSaverIterative, mapSaverIterative, setSaverIterative } from "./objectSaverIterative"; | ||
import { stringEncodeInto } from "./stringEncodeInto"; | ||
export function saveValueIterative(externalArgs, carrier, referencedExistingPointers, initialValuePtrToPtr, initialValue) { | ||
const valuesToSave = [initialValue]; | ||
const pointersToSaveTo = [initialValuePtrToPtr]; | ||
const { | ||
heap: { | ||
Uint32Array: uint32 | ||
}, | ||
allocator, | ||
heap | ||
} = carrier; | ||
export function saveValue(externalArgs, carrier, referencedPointers, value) { | ||
let valuePointer = 0; | ||
let maybeOurPointer; | ||
while (valuesToSave.length !== 0) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const valueToSave = valuesToSave.pop(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
if (value === undefined) { | ||
return UNDEFINED_KNOWN_ADDRESS; | ||
} | ||
const ptrToPtrToSaveTo = pointersToSaveTo.pop(); // Handler well-known values | ||
if (value === null) { | ||
return NULL_KNOWN_ADDRESS; | ||
} | ||
if (valueToSave === undefined) { | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
if (value === true) { | ||
return TRUE_KNOWN_ADDRESS; | ||
} | ||
if (valueToSave === null) { | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = NULL_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
if (value === false) { | ||
return FALSE_KNOWN_ADDRESS; | ||
} | ||
if (valueToSave === true) { | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = TRUE_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
if (isPrimitive(value)) { | ||
const entry = primitiveValueToEntry(value); | ||
valuePointer = appendEntry(externalArgs, carrier, entry); | ||
} else if (maybeOurPointer = getOurPointerIfApplicable(value, carrier.allocator)) { | ||
valuePointer = maybeOurPointer; | ||
referencedPointers.push(valuePointer); | ||
} else if (Array.isArray(value)) { | ||
valuePointer = arraySaver(externalArgs, carrier, referencedPointers, value); | ||
} else if (value instanceof Date) { | ||
valuePointer = appendEntry(externalArgs, carrier, { | ||
type: ENTRY_TYPE.DATE, | ||
refsCount: 1, | ||
value: value.getTime() | ||
}); | ||
} else if (value instanceof Map) { | ||
valuePointer = mapSaver(externalArgs, carrier, referencedPointers, value); | ||
} else if (value instanceof Set) { | ||
valuePointer = setSaver(externalArgs, carrier, value); | ||
} else if (typeof value === "object") { | ||
valuePointer = objectSaver(externalArgs, carrier, referencedPointers, value); | ||
} else { | ||
throw new Error("unsupported yet"); | ||
if (valueToSave === false) { | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = FALSE_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
switch (typeof valueToSave) { | ||
case "number": | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = allocator.calloc(number_size); | ||
number_set_all(heap, uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], ENTRY_TYPE.NUMBER, valueToSave); | ||
continue; | ||
case "string": | ||
// eslint-disable-next-line no-case-declarations | ||
const stringBytesLength = strByteLength(valueToSave); // eslint-disable-next-line no-case-declarations | ||
const stringDataPointer = allocator.calloc(stringBytesLength); | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = allocator.calloc(string_size); | ||
stringEncodeInto(heap.Uint8Array, stringDataPointer, valueToSave); | ||
string_set_all(heap, uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], ENTRY_TYPE.STRING, stringBytesLength, stringDataPointer); | ||
continue; | ||
case "bigint": | ||
if (valueToSave > MAX_64_BIG_INT || valueToSave < -MAX_64_BIG_INT) { | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; // Maybe don't make undefined but throw, or clamp | ||
// throw new Error("MAX_64_BIG_INT"); | ||
} | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = allocator.calloc(bigint_size); | ||
bigint_set_all(heap, uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], valueToSave > 0 ? ENTRY_TYPE.BIGINT_POSITIVE : ENTRY_TYPE.BIGINT_NEGATIVE, valueToSave * (valueToSave > 0 ? BigInt("1") : BigInt("-1"))); | ||
continue; | ||
case "function": | ||
// Nope Nope Nope | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
case "symbol": | ||
// not supported, write undefined | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
// // we will never get here | ||
// case "undefined": | ||
// continue; | ||
// // we will never get here | ||
// case "boolean": | ||
// continue; | ||
} | ||
const maybeOurPointerFromSymbol = getOurPointerIfApplicable(valueToSave, carrier.allocator); | ||
if (maybeOurPointerFromSymbol) { | ||
referencedExistingPointers.push(maybeOurPointerFromSymbol); | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = maybeOurPointerFromSymbol; | ||
continue; | ||
} | ||
if (Array.isArray(valueToSave)) { | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = arraySaverIterative(externalArgs.arrayAdditionalAllocation, carrier, valuesToSave, pointersToSaveTo, valueToSave); | ||
continue; | ||
} | ||
if (valueToSave instanceof Date) { | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = allocator.calloc(date_size); | ||
date_set_all(heap, heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], ENTRY_TYPE.DATE, 1, 0, valueToSave.getTime()); | ||
continue; | ||
} | ||
if (valueToSave instanceof Map) { | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = mapSaverIterative(externalArgs, carrier, valuesToSave, pointersToSaveTo, valueToSave); | ||
continue; | ||
} | ||
if (valueToSave instanceof Set) { | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = setSaverIterative(externalArgs, carrier, valueToSave); | ||
continue; | ||
} // Plain object? I hope so | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT] = objectSaverIterative(externalArgs, carrier, valuesToSave, pointersToSaveTo, valueToSave); | ||
} | ||
return valuePointer; | ||
} |
@@ -1,5 +0,5 @@ | ||
import { ExternalArgs, GlobalCarrier, MapEntry, InternalAPI } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier, InternalAPI } from "./interfaces"; | ||
import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
export declare class SetWrapper<K extends string | number> extends BaseProxyTrap<MapEntry> implements Set<K> { | ||
export declare class SetWrapper<K extends string | number> extends BaseProxyTrap implements Set<K> { | ||
clear(): void; | ||
@@ -6,0 +6,0 @@ forEach(callbackfn: (key: K, key2: K, map: Set<K>) => void, thisArg?: any): void; |
@@ -7,2 +7,3 @@ import { deleteObjectPropertyEntryByKey, objectSet, mapOrSetClear } from "./objectWrapperHelpers"; | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { object_pointerToHashMap_get } from "./generatedStructs"; | ||
export class SetWrapper extends BaseProxyTrap { | ||
@@ -20,3 +21,3 @@ clear() { | ||
get size() { | ||
return hashMapSize(this.carrier, this.entry.value); | ||
return hashMapSize(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer)); | ||
} | ||
@@ -29,4 +30,4 @@ | ||
*entries() { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier, this.entry.value)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer))) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
const key = entryToFinalJavaScriptValue(this.externalArgs, this.carrier, t.keyPointer); | ||
@@ -38,4 +39,4 @@ yield [key, key]; | ||
*keys() { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier, this.entry.value)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer))) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
yield entryToFinalJavaScriptValue(this.externalArgs, this.carrier, t.keyPointer); | ||
@@ -46,4 +47,4 @@ } | ||
*values() { | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier, this.entry.value)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
for (const nodePointer of hashmapNodesPointerIterator(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer))) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
yield entryToFinalJavaScriptValue(this.externalArgs, this.carrier, t.keyPointer); | ||
@@ -70,3 +71,3 @@ } | ||
return hashMapNodeLookup(this.carrier, this.entry.value, p) !== 0; | ||
return hashMapNodeLookup(this.carrier.heap, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p) !== 0; | ||
} | ||
@@ -80,3 +81,3 @@ | ||
allocationsTransaction(() => { | ||
objectSet(this.externalArgs, this.carrier, this.entry.value, p, undefined); | ||
objectSet(this.externalArgs, this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p, undefined); | ||
}, this.carrier.allocator); | ||
@@ -91,3 +92,3 @@ return this; | ||
return deleteObjectPropertyEntryByKey(this.externalArgs, this.carrier, this.entry.value, p); | ||
return deleteObjectPropertyEntryByKey(this.carrier, object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), p); | ||
} | ||
@@ -94,0 +95,0 @@ |
@@ -1,16 +0,2 @@ | ||
import { ExternalArgsApi, ExternalArgs } from "./interfaces"; | ||
/** | ||
* **UNRELIABLE YET** | ||
* | ||
* Calculate the size (bytes) of the given value. | ||
* Also validates that the value is saveable | ||
*/ | ||
export declare function sizeOf(externalArgs: ExternalArgsApi, value: any): number; | ||
interface CheckerResult { | ||
memoryAllocated: number; | ||
numberOfAllocations: number; | ||
} | ||
export declare function sizeOfObject(externalArgs: ExternalArgs, objectToSave: any): CheckerResult; | ||
export declare function sizeOfValue(externalArgs: ExternalArgs, value: any): CheckerResult; | ||
export {}; | ||
export declare function sizeOf(): number; | ||
//# sourceMappingURL=sizeOf.d.ts.map |
@@ -1,120 +0,183 @@ | ||
import { externalArgsApiToExternalArgsApi, isPrimitive, primitiveValueToEntry, align } from "./utils"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { LINKED_LIST_MACHINE, LINKED_LIST_ITEM_MACHINE } from "./linkedList/linkedList"; | ||
import { MAP_MACHINE, NODE_MACHINE } from "./hashmap/memoryLayout"; | ||
import { sizeOfEntry } from "./store"; | ||
/** | ||
* **UNRELIABLE YET** | ||
* | ||
* Calculate the size (bytes) of the given value. | ||
* Also validates that the value is saveable | ||
*/ | ||
export function sizeOf(externalArgs, value) { | ||
const temp = sizeOfValue(externalArgsApiToExternalArgsApi(externalArgs), value); | ||
return temp.memoryAllocated + temp.numberOfAllocations * 8; | ||
} | ||
function sizeOfArray(externalArgs, arrayToSave) { | ||
let memoryAllocated = 0; | ||
let numberOfAllocations = 0; | ||
memoryAllocated += (arrayToSave.length + externalArgs.arrayAdditionalAllocation) * Uint32Array.BYTES_PER_ELEMENT; | ||
numberOfAllocations += 1; | ||
const arrayStartEntry = { | ||
type: ENTRY_TYPE.ARRAY, | ||
value: 0, | ||
refsCount: 0, | ||
allocatedLength: arrayToSave.length + externalArgs.arrayAdditionalAllocation, | ||
length: arrayToSave.length | ||
}; | ||
memoryAllocated += align(sizeOfEntry(arrayStartEntry)); | ||
numberOfAllocations += 1; | ||
for (const item of arrayToSave) { | ||
const r = sizeOfValue(externalArgs, item); | ||
memoryAllocated += r.memoryAllocated; | ||
numberOfAllocations += r.numberOfAllocations; | ||
} | ||
return { | ||
memoryAllocated, | ||
numberOfAllocations | ||
}; | ||
} | ||
export function sizeOfObject(externalArgs, objectToSave) { | ||
let memoryAllocated = 0; | ||
let numberOfAllocations = 0; | ||
const objectEntries = Object.entries(objectToSave); | ||
const r = sizeOfHashmap(externalArgs, objectEntries.map(([key]) => key)); | ||
memoryAllocated += r.memoryAllocated; | ||
numberOfAllocations += r.allocations; | ||
for (const [value] of objectEntries) { | ||
const r = sizeOfValue(externalArgs, value); | ||
memoryAllocated += r.memoryAllocated; | ||
numberOfAllocations += r.numberOfAllocations; | ||
} | ||
const objectStartEntry = { | ||
type: ENTRY_TYPE.OBJECT, | ||
refsCount: 0, | ||
value: 0 | ||
}; | ||
memoryAllocated += align(sizeOfEntry(objectStartEntry)); | ||
numberOfAllocations += 1; | ||
return { | ||
memoryAllocated, | ||
numberOfAllocations | ||
}; | ||
} | ||
export function sizeOfValue(externalArgs, value) { | ||
if (value === undefined || value === null || value === true || value === false) { | ||
return { | ||
memoryAllocated: 0, | ||
numberOfAllocations: 0 | ||
}; | ||
} | ||
if (isPrimitive(value)) { | ||
const entry = primitiveValueToEntry(value); | ||
return { | ||
memoryAllocated: align(sizeOfEntry(entry)), | ||
numberOfAllocations: 1 | ||
}; | ||
} else if (Array.isArray(value)) { | ||
return sizeOfArray(externalArgs, value); | ||
} else if (value instanceof Date) { | ||
return { | ||
memoryAllocated: align(sizeOfEntry({ | ||
type: ENTRY_TYPE.DATE, | ||
refsCount: 0, | ||
value: value.getTime() | ||
})), | ||
numberOfAllocations: 1 | ||
}; | ||
} else if (typeof value === "object") { | ||
return sizeOfObject(externalArgs, value); | ||
} else { | ||
throw new Error("unsupported yet"); | ||
} | ||
} // @todo what if rehash will happen on initial insert of value? | ||
function sizeOfHashmap(externalArgs, keysArray) { | ||
const linkedListBaseAllocationsSize = align(LINKED_LIST_MACHINE.map.SIZE_OF) + // end marker | ||
align(LINKED_LIST_ITEM_MACHINE.map.SIZE_OF); | ||
const linkedListBaseAllocations = 2; | ||
const linkedListItemsAllocations = keysArray.length; | ||
const linkedListItemsAllocationsSize = keysArray.length * align(LINKED_LIST_ITEM_MACHINE.map.SIZE_OF); | ||
const hashMapBaseAllocations = 2; | ||
const hashMapBaseAllocationsSize = align(MAP_MACHINE.map.SIZE_OF) + align(externalArgs.hashMapMinInitialCapacity * Uint32Array.BYTES_PER_ELEMENT); | ||
const hashMapNodesAllocations = keysArray.length; | ||
const hashMapNodesAllocationsSize = align(NODE_MACHINE.map.SIZE_OF) * keysArray.length; | ||
const hashMapKeysSize = keysArray.map(k => sizeOfEntry(primitiveValueToEntry(k))).reduce((p, c) => { | ||
return p + align(c); | ||
}, 0); | ||
return { | ||
allocations: linkedListBaseAllocations + linkedListItemsAllocations + hashMapBaseAllocations + hashMapNodesAllocations + keysArray.length, | ||
memoryAllocated: linkedListBaseAllocationsSize + linkedListItemsAllocationsSize + hashMapBaseAllocationsSize + hashMapNodesAllocationsSize + hashMapKeysSize | ||
}; | ||
} | ||
/* istanbul ignore file */ | ||
export function sizeOf() { | ||
console.warn("FAKE VALUE"); | ||
return Math.pow(2, 24); | ||
} // import { | ||
// ExternalArgsApi, | ||
// ExternalArgs, | ||
// ArrayEntry, | ||
// ObjectEntry, | ||
// } from "./interfaces"; | ||
// import { | ||
// externalArgsApiToExternalArgsApi, | ||
// isPrimitive, | ||
// primitiveValueToEntry, | ||
// align, | ||
// } from "./utils"; | ||
// import { ENTRY_TYPE } from "./entry-types"; | ||
// import { sizeOfEntry } from "./store"; | ||
// import { | ||
// linkedList_size, | ||
// linkedListItem_size, | ||
// hashmap_size, | ||
// hashmapNode_size, | ||
// } from "./generatedStructs"; | ||
// /** | ||
// * **UNRELIABLE YET** | ||
// * | ||
// * Calculate the size (bytes) of the given value. | ||
// * Also validates that the value is saveable | ||
// */ | ||
// export function sizeOf(externalArgs: ExternalArgsApi, value: any) { | ||
// const temp = sizeOfValue( | ||
// externalArgsApiToExternalArgsApi(externalArgs), | ||
// value | ||
// ); | ||
// return temp.memoryAllocated + temp.numberOfAllocations * 8; | ||
// } | ||
// interface CheckerResult { | ||
// memoryAllocated: number; | ||
// numberOfAllocations: number; | ||
// } | ||
// function sizeOfArray( | ||
// externalArgs: ExternalArgs, | ||
// arrayToSave: Array<any> | ||
// ): CheckerResult { | ||
// let memoryAllocated = 0; | ||
// let numberOfAllocations = 0; | ||
// memoryAllocated += | ||
// (arrayToSave.length + externalArgs.arrayAdditionalAllocation) * | ||
// Uint32Array.BYTES_PER_ELEMENT; | ||
// numberOfAllocations += 1; | ||
// const arrayStartEntry: ArrayEntry = { | ||
// type: ENTRY_TYPE.ARRAY, | ||
// value: 0, | ||
// refsCount: 0, | ||
// allocatedLength: | ||
// arrayToSave.length + externalArgs.arrayAdditionalAllocation, | ||
// length: arrayToSave.length, | ||
// }; | ||
// memoryAllocated += align(sizeOfEntry(arrayStartEntry)); | ||
// numberOfAllocations += 1; | ||
// for (const item of arrayToSave) { | ||
// const r = sizeOfValue(externalArgs, item); | ||
// memoryAllocated += r.memoryAllocated; | ||
// numberOfAllocations += r.numberOfAllocations; | ||
// } | ||
// return { | ||
// memoryAllocated, | ||
// numberOfAllocations, | ||
// }; | ||
// } | ||
// export function sizeOfObject( | ||
// externalArgs: ExternalArgs, | ||
// objectToSave: any | ||
// ): CheckerResult { | ||
// let memoryAllocated = 0; | ||
// let numberOfAllocations = 0; | ||
// const objectEntries = Object.entries(objectToSave); | ||
// const r = sizeOfHashmap( | ||
// externalArgs, | ||
// objectEntries.map(([key]) => key) | ||
// ); | ||
// memoryAllocated += r.memoryAllocated; | ||
// numberOfAllocations += r.allocations; | ||
// for (const [value] of objectEntries) { | ||
// const r = sizeOfValue(externalArgs, value); | ||
// memoryAllocated += r.memoryAllocated; | ||
// numberOfAllocations += r.numberOfAllocations; | ||
// } | ||
// const objectStartEntry: ObjectEntry = { | ||
// type: ENTRY_TYPE.OBJECT, | ||
// refsCount: 0, | ||
// value: 0, | ||
// }; | ||
// memoryAllocated += align(sizeOfEntry(objectStartEntry)); | ||
// numberOfAllocations += 1; | ||
// return { | ||
// memoryAllocated, | ||
// numberOfAllocations, | ||
// }; | ||
// } | ||
// export function sizeOfValue( | ||
// externalArgs: ExternalArgs, | ||
// value: any | ||
// ): CheckerResult { | ||
// if ( | ||
// value === undefined || | ||
// value === null || | ||
// value === true || | ||
// value === false | ||
// ) { | ||
// return { | ||
// memoryAllocated: 0, | ||
// numberOfAllocations: 0, | ||
// }; | ||
// } | ||
// if (isPrimitive(value)) { | ||
// const entry = primitiveValueToEntry(value); | ||
// return { | ||
// memoryAllocated: align(sizeOfEntry(entry)), | ||
// numberOfAllocations: 1, | ||
// }; | ||
// } else if (Array.isArray(value)) { | ||
// return sizeOfArray(externalArgs, value); | ||
// } else if (value instanceof Date) { | ||
// return { | ||
// memoryAllocated: align( | ||
// sizeOfEntry({ | ||
// type: ENTRY_TYPE.DATE, | ||
// refsCount: 0, | ||
// value: value.getTime(), | ||
// }) | ||
// ), | ||
// numberOfAllocations: 1, | ||
// }; | ||
// } else if (typeof value === "object") { | ||
// return sizeOfObject(externalArgs, value); | ||
// } else { | ||
// throw new Error("unsupported yet"); | ||
// } | ||
// } | ||
// // @todo what if rehash will happen on initial insert of value? | ||
// function sizeOfHashmap( | ||
// externalArgs: ExternalArgs, | ||
// keysArray: Array<string | number> | ||
// ) { | ||
// const linkedListBaseAllocationsSize = | ||
// align(linkedList_size) + | ||
// // end marker | ||
// align(linkedListItem_size); | ||
// const linkedListBaseAllocations = 2; | ||
// const linkedListItemsAllocations = keysArray.length; | ||
// const linkedListItemsAllocationsSize = | ||
// keysArray.length * align(linkedListItem_size); | ||
// const hashMapBaseAllocations = 2; | ||
// const hashMapBaseAllocationsSize = | ||
// align(hashmap_size) + | ||
// align( | ||
// externalArgs.hashMapMinInitialCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
// ); | ||
// const hashMapNodesAllocations = keysArray.length; | ||
// const hashMapNodesAllocationsSize = | ||
// align(hashmapNode_size) * keysArray.length; | ||
// const hashMapKeysSize = keysArray | ||
// .map((k) => sizeOfEntry(primitiveValueToEntry(k))) | ||
// .reduce((p, c) => { | ||
// return p + align(c); | ||
// }, 0); | ||
// return { | ||
// allocations: | ||
// linkedListBaseAllocations + | ||
// linkedListItemsAllocations + | ||
// hashMapBaseAllocations + | ||
// hashMapNodesAllocations + | ||
// keysArray.length, | ||
// memoryAllocated: | ||
// linkedListBaseAllocationsSize + | ||
// linkedListItemsAllocationsSize + | ||
// hashMapBaseAllocationsSize + | ||
// hashMapNodesAllocationsSize + | ||
// hashMapKeysSize, | ||
// }; | ||
// } |
@@ -1,23 +0,14 @@ | ||
import { Entry, primitive, GlobalCarrier } from "./interfaces"; | ||
import { GlobalCarrier } from "./interfaces"; | ||
import { ExternalArgs } from "./interfaces"; | ||
import { Heap } from "../structsGenerator/consts"; | ||
export declare function initializeArrayBuffer(arrayBuffer: ArrayBuffer): void; | ||
export declare function sizeOfEntry(entry: Entry): number; | ||
export declare function writeEntry(carrier: GlobalCarrier, startingCursor: number, entry: Entry): void; | ||
export declare function appendEntry(externalArgs: ExternalArgs, carrier: GlobalCarrier, entry: Entry): number; | ||
export declare function readEntry(carrier: GlobalCarrier, startingCursor: number): Entry; | ||
export declare function canReuseMemoryOfEntry(entryA: Entry, value: primitive): boolean; | ||
export declare function writeValueInPtrToPtr(externalArgs: ExternalArgs, carrier: GlobalCarrier, ptrToPtr: number, value: any): { | ||
referencedPointers: number[]; | ||
existingEntryPointer: number; | ||
existingValueEntry: import("./interfaces").StringEntry | import("./interfaces").NumberEntry | import("./interfaces").BigIntPositiveEntry | import("./interfaces").BigIntNegativeEntry | import("./interfaces").ObjectEntry | import("./interfaces").ArrayEntry | import("./interfaces").DateEntry | import("./interfaces").MapEntry | import("./interfaces").SetEntry | undefined; | ||
} | undefined; | ||
export declare function writeValueInPtrToPtrAndHandleMemory(externalArgs: ExternalArgs, carrier: GlobalCarrier, ptrToPtr: number, value: any): void; | ||
export declare function handleArcForDeletedValuePointer(externalArgs: ExternalArgs, carrier: GlobalCarrier, deletedValuePointer: number): void; | ||
export declare function incrementRefCount(externalArgs: ExternalArgs, carrier: GlobalCarrier, entryPointer: number): number; | ||
export declare function decrementRefCount(externalArgs: ExternalArgs, carrier: GlobalCarrier, entryPointer: number): number; | ||
export declare function getRefCount(carrier: GlobalCarrier, entryPointer: number): number; | ||
export declare function setRefCount(carrier: GlobalCarrier, entryPointer: number, newRefCount: number): number; | ||
export declare function writeValueInPtrToPtr(externalArgs: ExternalArgs, carrier: GlobalCarrier, ptrToPtr: number, value: unknown): number[]; | ||
export declare function writeValueInPtrToPtrAndHandleMemory(externalArgs: ExternalArgs, carrier: GlobalCarrier, ptrToPtr: number, value: unknown): void; | ||
export declare function handleArcForDeletedValuePointer(carrier: GlobalCarrier, deletedValuePointer: number): void; | ||
export declare function incrementRefCount(heap: Heap, entryPointer: number): number; | ||
export declare function decrementRefCount(heap: Heap, entryPointer: number): number; | ||
export declare function getObjectValuePtrToPtr(pointerToEntry: number): number; | ||
export declare function memComp(uint8: Uint8Array, aStart: number, bStart: number, length: number): boolean; | ||
export declare function compareStringOrNumberEntriesInPlace(carrier: GlobalCarrier, entryAPointer: number, entryBPointer: number): boolean; | ||
export declare function compareStringOrNumberEntriesInPlace(heap: Heap, entryAPointer: number, entryBPointer: number): boolean; | ||
export declare function readNumberOrString(heap: Heap, pointer: number): string | number; | ||
//# sourceMappingURL=store.d.ts.map |
@@ -1,10 +0,8 @@ | ||
import { ENTRY_TYPE, isPrimitiveEntryType } from "./entry-types"; | ||
import { isPrimitive, primitiveValueToEntry, isKnownAddressValuePointer } from "./utils"; | ||
import { BigInt64OverflowError } from "./exceptions"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { isKnownAddressValuePointer, isTypeWithRC } from "./utils"; | ||
import { INITIAL_ENTRY_POINTER_TO_POINTER, INITIAL_ENTRY_POINTER_VALUE } from "./consts"; | ||
import { saveValue } from "./saveValue"; | ||
import { getAllLinkedAddresses } from "./getAllLinkedAddresses"; | ||
import { stringEncodeInto } from "./stringEncodeInto"; | ||
import { stringDecode } from "./stringDecode"; | ||
const MAX_64_BIG_INT = BigInt("0xFFFFFFFFFFFFFFFF"); | ||
import { typeAndRc_refsCount_get, typeAndRc_refsCount_set, typeOnly_type_get, number_value_get, string_bytesLength_get, string_charsPointer_get } from "./generatedStructs"; | ||
import { readString } from "./readString"; | ||
import { saveValueIterative } from "./saveValue"; | ||
export function initializeArrayBuffer(arrayBuffer) { | ||
@@ -14,310 +12,83 @@ const uint32 = new Uint32Array(arrayBuffer); | ||
uint32[INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT] = INITIAL_ENTRY_POINTER_VALUE; | ||
} | ||
export function sizeOfEntry(entry) { | ||
let cursor = 0; // type | ||
} // /* istanbul ignore next */ | ||
// export function sizeOfEntry(entry: Entry) { | ||
// let cursor = 0; | ||
// // type | ||
// cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// switch (entry.type) { | ||
// case ENTRY_TYPE.NUMBER: | ||
// cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// break; | ||
// case ENTRY_TYPE.STRING: | ||
// // string length | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// cursor += entry.allocatedBytes; | ||
// // oh boy. i don't want to change it now, but no choice | ||
// // @todo: this is incorrect? should be Math.max | ||
// // cursor += entry.allocatedBytes; | ||
// break; | ||
// case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
// case ENTRY_TYPE.BIGINT_POSITIVE: | ||
// if (entry.value > MAX_64_BIG_INT || entry.value < -MAX_64_BIG_INT) { | ||
// throw new BigInt64OverflowError(); | ||
// } | ||
// cursor += BigInt64Array.BYTES_PER_ELEMENT; | ||
// break; | ||
// case ENTRY_TYPE.OBJECT: | ||
// case ENTRY_TYPE.MAP: | ||
// case ENTRY_TYPE.SET: | ||
// // ref count | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // pointer | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// break; | ||
// case ENTRY_TYPE.ARRAY: | ||
// // refsCount | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // pointer | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // length | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // allocated length | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// break; | ||
// case ENTRY_TYPE.DATE: | ||
// // timestamp | ||
// cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// // ref count | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// break; | ||
// default: | ||
// // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
// } | ||
// return cursor; | ||
// } | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
switch (entry.type) { | ||
case ENTRY_TYPE.NUMBER: | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.STRING: | ||
// string length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
cursor += entry.allocatedBytes; // oh boy. i don't want to change it now, but no choice | ||
// @todo: this is incorrect? should be Math.max | ||
// cursor += entry.allocatedBytes; | ||
break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
if (entry.value > MAX_64_BIG_INT || entry.value < -MAX_64_BIG_INT) { | ||
throw new BigInt64OverflowError(); | ||
} | ||
cursor += BigInt64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.MAP: | ||
case ENTRY_TYPE.SET: | ||
// ref count | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; // pointer | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.ARRAY: | ||
// refsCount | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; // pointer | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; // length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; // allocated length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.DATE: | ||
// timestamp | ||
cursor += Float64Array.BYTES_PER_ELEMENT; // ref count | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
default: | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
} | ||
return cursor; | ||
} | ||
export function writeEntry(carrier, startingCursor, entry) { | ||
let cursor = startingCursor; // let writtenDataSizeInBytes = 0; | ||
// write type | ||
// undo on throw ? | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT] = entry.type; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
switch (entry.type) { | ||
case ENTRY_TYPE.NUMBER: | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.STRING: | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.allocatedBytes; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; // eslint-disable-next-line no-case-declarations | ||
const writtenBytes = stringEncodeInto(carrier.uint8, cursor, entry.value); | ||
if (writtenBytes !== entry.allocatedBytes) { | ||
// eslint-disable-next-line no-undef | ||
console.warn({ | ||
value: entry.value, | ||
writtenBytes, | ||
allocatedBytes: entry.allocatedBytes | ||
}, true); | ||
throw new Error("WTF???"); | ||
} | ||
cursor += entry.allocatedBytes; | ||
break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
if (entry.value > MAX_64_BIG_INT || entry.value < -MAX_64_BIG_INT) { | ||
throw new BigInt64OverflowError(); | ||
} | ||
carrier.bigUint64[cursor / BigUint64Array.BYTES_PER_ELEMENT] = entry.type === ENTRY_TYPE.BIGINT_NEGATIVE ? -entry.value : entry.value; | ||
cursor += BigUint64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.SET: | ||
case ENTRY_TYPE.MAP: | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.refsCount; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.ARRAY: | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.refsCount; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.length; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.allocatedLength; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.DATE: | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.refsCount; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
default: | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
} | ||
} | ||
export function appendEntry(externalArgs, carrier, entry) { | ||
const size = sizeOfEntry(entry); | ||
const memoryAddress = carrier.allocator.calloc(size); | ||
writeEntry(carrier, memoryAddress, entry); | ||
return memoryAddress; | ||
} | ||
export function readEntry(carrier, startingCursor) { | ||
let cursor = startingCursor; | ||
const entryType = carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
const entry = { | ||
type: entryType, | ||
value: undefined | ||
}; // let writtenDataSizeInBytes = 0; | ||
switch (entryType) { | ||
// handled by well-known addresses | ||
// case ENTRY_TYPE.UNDEFINED: | ||
// break; | ||
// case ENTRY_TYPE.NULL: | ||
// break; | ||
// case ENTRY_TYPE.BOOLEAN: | ||
// entry.value = carrier.dataView.getUint8(cursor) !== 0; | ||
// cursor += Uint8Array.BYTES_PER_ELEMENT; | ||
// break; | ||
case ENTRY_TYPE.NUMBER: | ||
entry.value = carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.STRING: | ||
// eslint-disable-next-line no-case-declarations | ||
const stringLength = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
entry.allocatedBytes = stringLength; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; // decode fails with zero length array | ||
if (stringLength > 0) { | ||
// this wrapping is needed until: | ||
// https://github.com/whatwg/encoding/issues/172 | ||
// eslint-disable-next-line no-case-declarations | ||
// const tempAB = new ArrayBuffer(stringLength, true); | ||
// arrayBufferCopyTo(dataView.buffer, cursor, stringLength, tempAB, 0, true); | ||
entry.value = stringDecode(carrier.uint8, cursor, stringLength); | ||
} else { | ||
entry.value = ""; | ||
} | ||
cursor += stringLength; | ||
break; | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
entry.value = carrier.bigUint64[cursor / BigUint64Array.BYTES_PER_ELEMENT]; | ||
cursor += BigUint64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
entry.value = -carrier.bigUint64[cursor / BigUint64Array.BYTES_PER_ELEMENT]; | ||
cursor += BigUint64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.MAP: | ||
case ENTRY_TYPE.SET: | ||
entry.refsCount = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.value = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.ARRAY: | ||
entry.refsCount = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.value = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.length = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.allocatedLength = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.DATE: | ||
entry.value = carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
entry.refsCount = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
default: | ||
throw new Error(ENTRY_TYPE[entryType] + " Not implemented yet"); | ||
} | ||
return entry; | ||
} | ||
export function canReuseMemoryOfEntry(entryA, value) { | ||
const typeofTheValue = typeof value; // number & bigint 64 are the same size | ||
if ((entryA.type === ENTRY_TYPE.BIGINT_NEGATIVE || entryA.type === ENTRY_TYPE.BIGINT_POSITIVE || entryA.type === ENTRY_TYPE.NUMBER) && (typeofTheValue === "bigint" || typeofTheValue === "number")) { | ||
return true; | ||
} // kill for strings for now | ||
// if ( | ||
// entryA.type === ENTRY_TYPE.STRING && | ||
// typeofTheValue === "string" && | ||
// entryA.allocatedBytes >= strByteLength(value as string) | ||
// ) { | ||
// return true; | ||
// } | ||
return false; | ||
} | ||
export function writeValueInPtrToPtr(externalArgs, carrier, ptrToPtr, value) { | ||
const existingEntryPointer = carrier.uint32[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
let reuse = false; | ||
let existingValueEntry; | ||
const referencedPointers = []; // Might oom here | ||
if (!isKnownAddressValuePointer(existingEntryPointer)) { | ||
existingValueEntry = readEntry(carrier, existingEntryPointer); | ||
reuse = isPrimitive(value) && isPrimitiveEntryType(existingValueEntry.type) && canReuseMemoryOfEntry(existingValueEntry, value); | ||
} // try to re use memory | ||
if (reuse) { | ||
const newEntry = primitiveValueToEntry(value); | ||
writeEntry(carrier, existingEntryPointer, newEntry); | ||
} else { | ||
const referencedPointers = []; | ||
const newEntryPointer = saveValue(externalArgs, carrier, referencedPointers, value); | ||
carrier.uint32[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT] = newEntryPointer; | ||
return { | ||
referencedPointers, | ||
existingEntryPointer, | ||
existingValueEntry | ||
}; | ||
} | ||
saveValueIterative(externalArgs, carrier, referencedPointers, ptrToPtr, value); | ||
return referencedPointers; | ||
} | ||
export function writeValueInPtrToPtrAndHandleMemory(externalArgs, carrier, ptrToPtr, value) { | ||
const { | ||
existingValueEntry = false, | ||
existingEntryPointer = 0, | ||
referencedPointers = [] | ||
} = writeValueInPtrToPtr(externalArgs, carrier, ptrToPtr, value) || {}; | ||
const existingEntryPointer = carrier.heap.Uint32Array[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT]; // Might oom here | ||
const referencedPointers = writeValueInPtrToPtr(externalArgs, carrier, ptrToPtr, value); // -- end of might oom | ||
// commit ref count changes of existing objects | ||
if (referencedPointers.length > 0) { | ||
for (const ptr of referencedPointers) { | ||
incrementRefCount(externalArgs, carrier, ptr); | ||
incrementRefCount(carrier.heap, ptr); | ||
} | ||
} | ||
if (existingValueEntry && "refsCount" in existingValueEntry) { | ||
const newRefCount = decrementRefCount(externalArgs, carrier, existingEntryPointer); | ||
handleArcForDeletedValuePointer(carrier, existingEntryPointer); | ||
} | ||
export function handleArcForDeletedValuePointer(carrier, deletedValuePointer) { | ||
const { | ||
heap, | ||
allocator | ||
} = carrier; // No memory to free/ARC | ||
if (newRefCount === 0) { | ||
const addressesToFree = getAllLinkedAddresses(carrier, false, existingEntryPointer); | ||
for (const address of addressesToFree.leafAddresses) { | ||
carrier.allocator.free(address); | ||
} | ||
for (const address of addressesToFree.arcAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
} | ||
} | ||
} else { | ||
carrier.allocator.free(existingEntryPointer); | ||
} | ||
} | ||
export function handleArcForDeletedValuePointer(externalArgs, carrier, deletedValuePointer) { | ||
// No memory to free/ARC | ||
if (isKnownAddressValuePointer(deletedValuePointer)) { | ||
@@ -327,78 +98,39 @@ return; | ||
const existingValueEntry = readEntry(carrier, deletedValuePointer); | ||
const entryType = typeOnly_type_get(heap, deletedValuePointer); | ||
if (existingValueEntry && "refsCount" in existingValueEntry) { | ||
const newRefCount = decrementRefCount(externalArgs, carrier, deletedValuePointer); | ||
if (!isTypeWithRC(entryType)) { | ||
if (entryType === ENTRY_TYPE.STRING) { | ||
allocator.free(string_charsPointer_get(carrier.heap, deletedValuePointer)); | ||
} | ||
if (newRefCount === 0) { | ||
const addressesToFree = getAllLinkedAddresses(carrier, false, deletedValuePointer); | ||
for (const address of addressesToFree.leafAddresses) { | ||
carrier.allocator.free(address); | ||
} | ||
for (const address of addressesToFree.arcAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
} | ||
} | ||
} else { | ||
carrier.allocator.free(deletedValuePointer); | ||
allocator.free(deletedValuePointer); | ||
return; | ||
} | ||
} | ||
export function incrementRefCount(externalArgs, carrier, entryPointer) { | ||
const entry = readEntry(carrier, entryPointer); | ||
if ("refsCount" in entry) { | ||
entry.refsCount += 1; | ||
writeEntry(carrier, entryPointer, entry); | ||
return entry.refsCount; | ||
if (decrementRefCount(heap, deletedValuePointer) > 0) { | ||
allocator.free(deletedValuePointer); | ||
return; | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function decrementRefCount(externalArgs, carrier, entryPointer) { | ||
const entry = readEntry(carrier, entryPointer); | ||
const { | ||
leafAddresses, | ||
arcAddresses | ||
} = getAllLinkedAddresses(carrier.heap, false, deletedValuePointer); | ||
if ("refsCount" in entry) { | ||
entry.refsCount -= 1; | ||
writeEntry(carrier, entryPointer, entry); | ||
return entry.refsCount; | ||
for (const address of leafAddresses) { | ||
allocator.free(address); | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function getRefCount(carrier, entryPointer) { | ||
const entry = readEntry(carrier, entryPointer); | ||
if ("refsCount" in entry) { | ||
return entry.refsCount; | ||
for (const address of arcAddresses) { | ||
decrementRefCount(heap, address); | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function setRefCount(carrier, entryPointer, newRefCount) { | ||
const entry = readEntry(carrier, entryPointer); | ||
if ("refsCount" in entry) { | ||
const prevCount = entry.refsCount; | ||
entry.refsCount = newRefCount; | ||
writeEntry(carrier, entryPointer, entry); | ||
return prevCount; | ||
} | ||
throw new Error("unexpected"); | ||
} // export function getObjectPropPtrToPtr( | ||
// { dataView }: GlobalCarrier, | ||
// pointerToEntry: number | ||
// ) { | ||
// const keyStringLength = dataView.getUint16(pointerToEntry + 1); | ||
// const valuePtrToPtr = | ||
// Uint16Array.BYTES_PER_ELEMENT + pointerToEntry + 1 + keyStringLength; | ||
// const nextPtrToPtr = valuePtrToPtr + Uint32Array.BYTES_PER_ELEMENT; | ||
// return { | ||
// valuePtrToPtr, | ||
// nextPtrToPtr | ||
// }; | ||
// } | ||
export function incrementRefCount(heap, entryPointer) { | ||
typeAndRc_refsCount_set(heap, entryPointer, typeAndRc_refsCount_get(heap, entryPointer) + 1); | ||
return typeAndRc_refsCount_get(heap, entryPointer); | ||
} | ||
export function decrementRefCount(heap, entryPointer) { | ||
typeAndRc_refsCount_set(heap, entryPointer, typeAndRc_refsCount_get(heap, entryPointer) - 1); | ||
return typeAndRc_refsCount_get(heap, entryPointer); | ||
} | ||
export function getObjectValuePtrToPtr(pointerToEntry) { | ||
@@ -421,7 +153,6 @@ return pointerToEntry + 1 + 1; | ||
} | ||
export function compareStringOrNumberEntriesInPlace(carrier, entryAPointer, entryBPointer) { | ||
let cursor = 0; | ||
const entryAType = carrier.float64[(entryAPointer + cursor) / Float64Array.BYTES_PER_ELEMENT]; | ||
const entryBType = carrier.float64[(entryBPointer + cursor) / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
export function compareStringOrNumberEntriesInPlace(heap, entryAPointer, entryBPointer) { | ||
typeOnly_type_get(heap, entryAPointer); | ||
const entryAType = typeOnly_type_get(heap, entryAPointer); | ||
const entryBType = typeOnly_type_get(heap, entryBPointer); | ||
@@ -433,15 +164,23 @@ if (entryAType !== entryBType) { | ||
if (entryAType === ENTRY_TYPE.STRING) { | ||
const aLength = carrier.uint32[(entryAPointer + cursor) / Uint32Array.BYTES_PER_ELEMENT]; | ||
const bLength = carrier.uint32[(entryBPointer + cursor) / Uint32Array.BYTES_PER_ELEMENT]; | ||
const aLength = string_bytesLength_get(heap, entryAPointer); | ||
const bLength = string_bytesLength_get(heap, entryBPointer); | ||
if (aLength !== bLength) { | ||
return false; | ||
} // string length | ||
} | ||
return memComp(heap.Uint8Array, string_charsPointer_get(heap, entryAPointer), string_charsPointer_get(heap, entryBPointer), aLength); | ||
} // numbers | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
return memComp(carrier.uint8, entryAPointer + cursor, entryBPointer + cursor, aLength); | ||
return number_value_get(heap, entryAPointer) === number_value_get(heap, entryBPointer); | ||
} | ||
export function readNumberOrString(heap, pointer) { | ||
const type = typeOnly_type_get(heap, pointer); | ||
if (type === ENTRY_TYPE.NUMBER) { | ||
return number_value_get(heap, pointer); | ||
} else { | ||
return readString(heap, pointer); | ||
} | ||
return carrier.float64[(entryAPointer + cursor) / Float64Array.BYTES_PER_ELEMENT] === carrier.float64[(entryBPointer + cursor) / Float64Array.BYTES_PER_ELEMENT]; | ||
} |
@@ -0,1 +1,2 @@ | ||
/* istanbul ignore file */ | ||
// https://github.com/anonyco/FastestSmallestTextEncoderDecoder/blob/master/EncoderDecoderTogether.src.js | ||
@@ -2,0 +3,0 @@ // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder#Polyfill |
@@ -16,3 +16,4 @@ export function arrayBuffer2HexArray(buffer, withByteNumber = false) { | ||
import { OutOfMemoryError } from "./exceptions"; | ||
import { MEM_POOL_START } from "./consts"; // extend pool and not monkey patch? need to think about it | ||
import { MEM_POOL_START } from "./consts"; | ||
import { createHeap } from "../structsGenerator/consts"; // extend pool and not monkey patch? need to think about it | ||
@@ -65,4 +66,4 @@ export function recordAllocations(operation, pool) { | ||
return allocation; | ||
}; // eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
}; // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
@@ -92,11 +93,6 @@ | ||
const carrier = { | ||
// dataView: new DataView(arrayBuffer), | ||
allocator, | ||
uint8: new Uint8Array(arrayBuffer), | ||
uint16: new Uint16Array(arrayBuffer), | ||
uint32: new Uint32Array(arrayBuffer), | ||
float64: new Float64Array(arrayBuffer), | ||
bigUint64: new BigUint64Array(arrayBuffer) | ||
heap: createHeap(arrayBuffer) | ||
}; | ||
return carrier; | ||
} |
@@ -1,5 +0,4 @@ | ||
import { primitive, Entry, ExternalArgs, InternalAPI, ExternalArgsApi } from "./interfaces"; | ||
import { ExternalArgs, InternalAPI, ExternalArgsApi } from "./interfaces"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { IMemPool } from "@thi.ng/malloc"; | ||
export declare function isPrimitive(value: unknown): value is primitive; | ||
export declare function primitiveValueToEntry(value: primitive): Entry; | ||
export declare function createKnownTypeGuard<T>(arr: ReadonlyArray<T>): (v: unknown) => v is T; | ||
@@ -11,5 +10,15 @@ export declare function invariant(condition: boolean, message: string): void; | ||
export declare function getInternalAPI(value: any): InternalAPI; | ||
/** | ||
* Incorrect length (too big) for emojis | ||
* @param str | ||
*/ | ||
export declare function strByteLength(str: string): number; | ||
export declare function align(value: number, alignTo?: number): number; | ||
export declare function isKnownAddressValuePointer(entryPointer: number): boolean; | ||
export declare function isTypeWithRC(type: ENTRY_TYPE): boolean; | ||
/** | ||
* | ||
* I hope It's reliable | ||
*/ | ||
export declare function isSupportedTopLevelValue(value: unknown): boolean; | ||
//# sourceMappingURL=utils.d.ts.map |
import { ENTRY_TYPE } from "./entry-types"; | ||
import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
import { UNDEFINED_KNOWN_ADDRESS, NULL_KNOWN_ADDRESS, TRUE_KNOWN_ADDRESS, FALSE_KNOWN_ADDRESS } from "./consts"; | ||
const primitives = ["string", "number", "bigint", "boolean", "undefined"]; | ||
export function isPrimitive(value) { | ||
if (primitives.includes(typeof value)) { | ||
return true; | ||
} | ||
if (value === null) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
export function primitiveValueToEntry(value) { | ||
if (typeof value === "string") { | ||
return { | ||
type: ENTRY_TYPE.STRING, | ||
value, | ||
allocatedBytes: strByteLength(value) | ||
}; | ||
} | ||
if (typeof value === "number") { | ||
return { | ||
type: ENTRY_TYPE.NUMBER, | ||
value | ||
}; | ||
} | ||
if (typeof value === "bigint") { | ||
return { | ||
type: value >= BigInt("0") ? ENTRY_TYPE.BIGINT_POSITIVE : ENTRY_TYPE.BIGINT_NEGATIVE, | ||
value | ||
}; | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function createKnownTypeGuard(arr) { | ||
@@ -79,2 +42,7 @@ return function knownTypeGuard(v) { | ||
} | ||
/** | ||
* Incorrect length (too big) for emojis | ||
* @param str | ||
*/ | ||
export function strByteLength(str) { | ||
@@ -95,2 +63,14 @@ let s = str.length; | ||
return entryPointer === UNDEFINED_KNOWN_ADDRESS || entryPointer === NULL_KNOWN_ADDRESS || entryPointer === TRUE_KNOWN_ADDRESS || entryPointer === FALSE_KNOWN_ADDRESS; | ||
} | ||
export function isTypeWithRC(type) { | ||
return type === ENTRY_TYPE.OBJECT || type === ENTRY_TYPE.ARRAY || type === ENTRY_TYPE.DATE || type === ENTRY_TYPE.MAP || type === ENTRY_TYPE.SET; | ||
} | ||
/** | ||
* | ||
* I hope It's reliable | ||
*/ | ||
export function isSupportedTopLevelValue(value) { | ||
return !(Array.isArray(value) || value instanceof Date || value instanceof Map || value instanceof Set || typeof value !== "object" || typeof value === null || // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
value.constructor.name !== "Object"); | ||
} |
@@ -0,1 +1,5 @@ | ||
/* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
/* istanbul ignore file */ | ||
// We can't run test with weakrefs yet | ||
const KEYS = 1; | ||
@@ -16,8 +20,20 @@ const VALUES = 2; | ||
const FinalizationSomething = typeof FinalizationRegistry !== "undefined" ? FinalizationRegistry : FinalizationGroup; | ||
this.group = new FinalizationSomething(iterator => { | ||
for (const key of iterator) { | ||
this.map.delete(key); | ||
this.group = new FinalizationSomething(iteratorOrKey => { | ||
// @ts-expect-error | ||
if (Symbol.iterator in iterable) { | ||
// @ts-expect-error | ||
for (const key of iteratorOrKey) { | ||
this.map.delete(key); | ||
if (this.externalFinalizer) { | ||
this.externalFinalizer(key); | ||
} | ||
} | ||
} else { | ||
// @ts-expect-error | ||
this.map.delete(iteratorOrKey); | ||
if (this.externalFinalizer) { | ||
this.externalFinalizer(key); | ||
// @ts-expect-error | ||
this.externalFinalizer(iteratorOrKey); | ||
} | ||
@@ -24,0 +40,0 @@ } |
{ | ||
"name": "@bnaya/objectbuffer", | ||
"description": "Object-like api, backed by an array buffer", | ||
"version": "0.0.0-a3f2701", | ||
"version": "0.0.0-ab8ba96", | ||
"main": "dist/objectbuffer.cjs.js", | ||
@@ -36,38 +36,41 @@ "module": "dist/index.js", | ||
"webpack-playground": "webpack-dev-server --mode=development --config playground/webpack.config.js --open", | ||
"generate-docs": "rimraf -f docs/generated/ && typedoc --out docs/generated/ --includes . --name objectbuffer --readme none --entryPoint \"\\\"index\\\"\" --excludeNotExported --ignoreCompilerErrors --mode modules src/index.ts" | ||
"generate-docs": "rimraf -f docs/generated/ && typedoc --mode library --readme none --out docs/generated/ --excludeNotExported --ignoreCompilerErrors src/index.ts", | ||
"generate-structs": "npx ts-node src/structsGenerator/run.ts" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.8.4", | ||
"@babel/core": "^7.9.0", | ||
"@babel/preset-env": "^7.9.5", | ||
"@babel/preset-typescript": "^7.9.0", | ||
"@types/benchmark": "^1.0.31", | ||
"@types/jest": "^25.2.1", | ||
"@babel/cli": "^7.10.1", | ||
"@babel/core": "^7.10.1", | ||
"@babel/node": "^7.10.1", | ||
"@babel/preset-env": "^7.10.1", | ||
"@babel/preset-typescript": "^7.10.1", | ||
"@types/benchmark": "^1.0.32", | ||
"@types/jest": "^25.2.3", | ||
"@types/node": "^12.12.21", | ||
"@typescript-eslint/eslint-plugin": "^2.27.0", | ||
"@typescript-eslint/parser": "^2.27.0", | ||
"@typescript-eslint/eslint-plugin": "^3.0.2", | ||
"@typescript-eslint/parser": "^3.0.2", | ||
"babel-loader": "^8.1.0", | ||
"benchmark": "^2.1.4", | ||
"concurrently": "^5.1.0", | ||
"concurrently": "^5.2.0", | ||
"core-js": "^3.6.5", | ||
"eslint": "^6.8.0", | ||
"eslint-config-prettier": "^6.10.1", | ||
"eslint-plugin-prettier": "^3.1.2", | ||
"eslint": "^7.1.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"gh-pages": "^2.2.0", | ||
"html-webpack-plugin": "^4.2.0", | ||
"html-webpack-plugin": "^4.3.0", | ||
"husky": "^4.2.5", | ||
"jest": "^25.3.0", | ||
"jest": "^26.0.1", | ||
"kind-of": "^6.0.3", | ||
"prettier": "^2.0.4", | ||
"prettier-eslint": "^9.0.1", | ||
"prettier": "^2.0.5", | ||
"prettier-eslint": "^10.1.1", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^2.4.0", | ||
"rollup": "^2.11.2", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"typedoc": "^0.17.4", | ||
"rollup-plugin-terser": "^6.1.0", | ||
"typedoc": "^0.17.7", | ||
"typedoc-plugin-markdown": "^2.2.17", | ||
"typescript": "^3.8.3", | ||
"webpack": "^4.42.1", | ||
"typescript": "^3.9.3", | ||
"webpack": "^4.43.0", | ||
"webpack-cli": "^3.3.11", | ||
"webpack-dev-server": "^3.10.3", | ||
"webpack-dev-server": "^3.11.0", | ||
"worker-loader": "^2.0.0", | ||
@@ -89,4 +92,4 @@ "yarn-deduplicate": "^2.0.0" | ||
"dependencies": { | ||
"@thi.ng/malloc": "^4.1.10" | ||
"@thi.ng/malloc": "^4.1.14" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
# ObjectBuffer: object-like API, backed by a [shared]arraybuffer | ||
# ObjectBuffer: object-like API, backed by a [shared]arraybuffer 👀 | ||
@@ -6,14 +6,17 @@ [![npm version](https://badge.fury.io/js/%40bnaya%2Fobjectbuffer.svg)](https://badge.fury.io/js/%40bnaya%2Fobjectbuffer) | ||
## The readme is for latest release `v0.10.0`.big refactor ongoing (allocator, hashmap) | ||
For Modern browsers and node. | ||
For Modern browsers and node. Zero direct dependencies. | ||
Save, read and update plain javascript objects into `ArrayBuffer` (And not only TypedArrays), using regular javascript object api, without serialization/deserialization, or pre-defined schema. | ||
In other words, It's an implementation of javascript objects in user-land. | ||
Save, read and update plain javascript objects into `ArrayBuffer` (And not only TypedArrays), using regular javascript object api, without serialization/deserialization. | ||
Look at it as a simple implementation of javascript objects in user-land. | ||
That's enables us to `transfer` or share objects in-memory with `WebWorker` without additional memory or serialization | ||
While the library is not `1.0`, it is usable. | ||
The library is still not `1.0`, but already usable, and will never offer full compatibility with plain js (`Symbol` and such) | ||
A core part of the library is an allocator, that allocates & free memory on the `ArrayBuffer` for us! | ||
The allocator in use is [@thi.ng/malloc](https://www.npmjs.com/package/@thi.ng/malloc), part of the amazing [thi.ng/umbrella](https://github.com/thi-ng/umbrella) project | ||
For in-depth overview of how things are implemented, see [implementation details document](docs/implementationDetails.md) | ||
## 🐉🐉🐉 Adventurers Beware 🐉🐉🐉 | ||
Using this library, and workers in general, will not necessarily make you code faster. | ||
First be sure where are your bottlenecks and if you don't have a better and more simple workaround. | ||
I personally also really like what's going on around the [main thread scheduling proposal](https://github.com/WICG/main-thread-scheduling) and [react userland scheduler](https://www.npmjs.com/package/scheduler) that powers concurrent react | ||
@@ -30,7 +33,3 @@ ## Quick example | ||
const myObject = createObjectBuffer( | ||
{ | ||
// available globally in the browser, or inside `util` in node | ||
textEncoder: new TextEncoder(), | ||
textDecoder: new TextDecoder() | ||
}, | ||
{}, | ||
// size in bytes | ||
@@ -84,15 +83,18 @@ 1024, | ||
* Date | ||
* Internal references (`foo.bar2 = foo.bar` will not create a copy) | ||
* BigInt | ||
* Internal references (`foo.bar2 = foo.bar` will not create a copy, but a reference) | ||
* Automatic reference counting, to dispose a value you need to use the `disposeWrapperObject` or to have WeakRef support | ||
* Internal equality between objects (`foo.bar === foo.bar` will be true) | ||
* global lock for shared memory using [Atomics](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics) (i hope its really working) | ||
* global lock for shared memory using [Atomics](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics) (I hope its really working) | ||
## Caveats & Limitations | ||
* Need to specify size for the `ArrayBuffer`. When exceed that size, exception will be thrown. (Can be extended with a utility function, but not automatically) | ||
* Not memory re-use. memory allocation is append based, or overwrite when possible [#21](https://github.com/Bnaya/objectbuffer/issues/21) | ||
* Object are implemented using simple linked list [#26](https://github.com/Bnaya/objectbuffer/issues/26) | ||
* Maps & Sets are not supported yet [#15](https://github.com/Bnaya/objectbuffer/issues/15) & [#24](https://github.com/Bnaya/objectbuffer/issues/24) | ||
* No prototype chain. objects props will simply be copied | ||
* Additional props on Array, Date, primitives will not be saved. | ||
* getters, setters, will not work/break the library | ||
* Need to specify size for the `ArrayBuffer`. When exceed that size, exception will be thrown. (Can be extended later with a utility function, but not automatically) | ||
* Size must be multiplication of 8 | ||
* Set, Map, Object keys can be only string or numbers. no symbols or other things | ||
* You can't save objects with circularities (But you can create them on objectbuffer) | ||
* No prototype chain. no methods on the objects | ||
* Adding getters, setters, will not work/break the library | ||
* deleting/removing the current key of Map/Set while iteration will make you skip the next key [#60](https://github.com/Bnaya/objectbuffer/issues/60) | ||
* unreliable_sizeOf is unreliable due to hashmap array side depends on actual keys, Also It's an expensive operation | ||
@@ -103,3 +105,3 @@ ### What's not working yet, but can be | ||
### What's probably never going to work (convince me otherwise ) | ||
### What's probably never going to work | ||
@@ -112,2 +114,10 @@ * Anything that cannot go into `JSON.stringify` | ||
There's a huge place for optimizations, code hygiene, and features! | ||
Feel free to open issues and maybe implementing missing parts | ||
Feel free to open issues and maybe implementing missing parts. | ||
The code is Written in TypeScript 🦾, but the semantics are more like `C` 🥵 | ||
Have a look on the [issues](https://github.com/Bnaya/objectbuffer/issues) and see if you find something interesting | ||
## If you came this far, you better also look at: | ||
* [GoogleChromeLabs/buffer-backed-object](https://github.com/GoogleChromeLabs/buffer-backed-object#readme) | ||
* [FlatBuffers](https://google.github.io/flatbuffers/flatbuffers_guide_use_javascript.html) | ||
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
import { memoryStats } from "../internal/api"; | ||
import { memoryStats, disposeWrapperObject } from "../internal/api"; | ||
import { externalArgsApiToExternalArgsApi } from "../internal/utils"; | ||
@@ -12,4 +10,2 @@ | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -40,4 +36,160 @@ }); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`592`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`560`); | ||
}); | ||
test("splice 1", () => { | ||
const ob = createObjectBuffer<{ arr: (number | null)[] }>( | ||
{ arrayAdditionalAllocation: 10 }, | ||
1024, | ||
{ arr: [] }, | ||
{ useSharedArrayBuffer: false } | ||
); | ||
const usedAfterCreate = memoryStats(ob).used; | ||
expect(usedAfterCreate).toMatchInlineSnapshot(`320`); | ||
ob.arr.push(null, null, null, null, null, null, null, null, null, null); | ||
const usedAfterNullPush = memoryStats(ob).used; | ||
expect(usedAfterNullPush - usedAfterCreate).toMatchInlineSnapshot(`0`); | ||
const removedNulls = ob.arr.splice(0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); | ||
expect(removedNulls).toMatchInlineSnapshot(` | ||
Array [ | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
] | ||
`); | ||
const usedAfterReplaceNullsWithNumbers = memoryStats(ob).used; | ||
expect( | ||
usedAfterReplaceNullsWithNumbers - usedAfterCreate | ||
).toMatchInlineSnapshot(`240`); | ||
const removedNumbers = ob.arr.splice(0, 10); | ||
expect(removedNumbers).toMatchInlineSnapshot(` | ||
Array [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
5, | ||
6, | ||
7, | ||
8, | ||
9, | ||
10, | ||
] | ||
`); | ||
const usedAfterRemoveNumbers = memoryStats(ob).used; | ||
expect(usedAfterRemoveNumbers - usedAfterCreate).toMatchInlineSnapshot(`0`); | ||
}); | ||
test("splice 2", () => { | ||
const ob = createObjectBuffer<{ arr: any }>( | ||
{ arrayAdditionalAllocation: 0 }, | ||
1024 * 2, | ||
{ arr: [] }, | ||
{ useSharedArrayBuffer: false } | ||
); | ||
const usedAfterCreate = memoryStats(ob).used; | ||
expect(usedAfterCreate).toMatchInlineSnapshot(`272`); | ||
ob.arr.push(null, null, null, null, null, null, null, null, null, null); | ||
const usedAfterNullPush = memoryStats(ob).used; | ||
expect(usedAfterNullPush - usedAfterCreate).toMatchInlineSnapshot(`48`); | ||
const removedNulls = ob.arr.splice( | ||
0, | ||
10, | ||
[1], | ||
[2], | ||
[3], | ||
[4], | ||
[5], | ||
[6], | ||
[7], | ||
[8], | ||
[9], | ||
[10] | ||
); | ||
expect(removedNulls).toMatchInlineSnapshot(` | ||
Array [ | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null, | ||
] | ||
`); | ||
const usedAfterReplaceNullsWithNumbers = memoryStats(ob).used; | ||
expect( | ||
usedAfterReplaceNullsWithNumbers - usedAfterCreate | ||
).toMatchInlineSnapshot(`768`); | ||
const removedArrays = ob.arr.splice(0, 10); | ||
expect(removedArrays).toMatchInlineSnapshot(` | ||
Array [ | ||
Array [ | ||
1, | ||
], | ||
Array [ | ||
2, | ||
], | ||
Array [ | ||
3, | ||
], | ||
Array [ | ||
4, | ||
], | ||
Array [ | ||
5, | ||
], | ||
Array [ | ||
6, | ||
], | ||
Array [ | ||
7, | ||
], | ||
Array [ | ||
8, | ||
], | ||
Array [ | ||
9, | ||
], | ||
Array [ | ||
10, | ||
], | ||
] | ||
`); | ||
const usedAfterRemovedArrays = memoryStats(ob).used; | ||
expect(usedAfterRemovedArrays - usedAfterCreate).toMatchInlineSnapshot( | ||
`448` | ||
); | ||
disposeWrapperObject(ob.arr); | ||
removedArrays.forEach((a: any) => disposeWrapperObject(a)); | ||
const usedDisposeArrays = memoryStats(ob).used; | ||
expect(usedDisposeArrays - usedAfterCreate).toMatchInlineSnapshot(`48`); | ||
}); | ||
}); |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -11,4 +9,2 @@ import { memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -49,4 +45,4 @@ }); | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`264`); | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`272`); | ||
}); | ||
}); |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -11,4 +9,2 @@ import { memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -28,2 +24,25 @@ }); | ||
expect(objectBuffer).toMatchInlineSnapshot(` | ||
Object { | ||
"arr": Array [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
], | ||
"arr2": Array [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
], | ||
"obj": Object { | ||
"a": 1, | ||
}, | ||
"obj2": Object { | ||
"a": 1, | ||
}, | ||
} | ||
`); | ||
expect(objectBuffer.arr2).toBe(objectBuffer.arr2); | ||
@@ -35,27 +54,4 @@ expect(objectBuffer.arr2).toBe(objectBuffer.arr); | ||
expect(objectBuffer).toMatchInlineSnapshot(` | ||
Object { | ||
"arr": Array [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
], | ||
"arr2": Array [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
], | ||
"obj": Object { | ||
"a": 1, | ||
}, | ||
"obj2": Object { | ||
"a": 1, | ||
}, | ||
} | ||
`); | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`776`); | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`856`); | ||
}); | ||
}); |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -10,6 +8,3 @@ import { memoryStats } from "../internal/api"; | ||
describe("Map", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
}); | ||
const externalArgs = externalArgsApiToExternalArgsApi({}); | ||
@@ -21,3 +16,3 @@ test("creation", () => { | ||
objectBuffer.foo = new Map([[1, "a"]]); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`592`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`560`); | ||
expect(objectBuffer.foo).toMatchInlineSnapshot(` | ||
@@ -36,3 +31,3 @@ Map { | ||
objectBuffer.foo.set("2", "b"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`440`); | ||
expect(objectBuffer.foo).toMatchInlineSnapshot(` | ||
@@ -60,7 +55,7 @@ Map { | ||
objectBuffer.foo.set("2", "b"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`440`); | ||
objectBuffer.foo.delete(1); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`592`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`544`); | ||
@@ -79,11 +74,12 @@ expect(objectBuffer.foo).toMatchInlineSnapshot(` | ||
objectBuffer.foo = new Map(); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`680`); | ||
const availSizeAfterCreation = memoryStats(objectBuffer).available; | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`664`); | ||
objectBuffer.foo.set(1, "a"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`592`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`560`); | ||
objectBuffer.foo.set("2", "b"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`440`); | ||
objectBuffer.foo.clear(); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`680`); | ||
expect(memoryStats(objectBuffer).available).toBe(availSizeAfterCreation); | ||
@@ -100,3 +96,3 @@ expect(objectBuffer.foo).toMatchInlineSnapshot(`Map {}`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`440`); | ||
@@ -103,0 +99,0 @@ expect(objectBuffer.foo).toMatchInlineSnapshot(` |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -11,4 +9,2 @@ import { resizeObjectBuffer, memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -25,4 +21,4 @@ }); | ||
Object { | ||
"available": 376, | ||
"used": 648, | ||
"available": 328, | ||
"used": 696, | ||
} | ||
@@ -40,4 +36,4 @@ `); | ||
Object { | ||
"available": 1400, | ||
"used": 648, | ||
"available": 1352, | ||
"used": 696, | ||
} | ||
@@ -49,4 +45,4 @@ `); | ||
Object { | ||
"available": 376, | ||
"used": 648, | ||
"available": 328, | ||
"used": 696, | ||
} | ||
@@ -58,4 +54,4 @@ `); | ||
Object { | ||
"available": 120, | ||
"used": 648, | ||
"available": 72, | ||
"used": 696, | ||
} | ||
@@ -66,8 +62,8 @@ `); | ||
expect(memoryStats(objectBuffer)).toMatchInlineSnapshot(` | ||
Object { | ||
"available": 1400, | ||
"used": 648, | ||
} | ||
`); | ||
Object { | ||
"available": 1352, | ||
"used": 696, | ||
} | ||
`); | ||
}); | ||
}); |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
import { memoryStats } from "../internal/api"; | ||
import { externalArgsApiToExternalArgsApi } from "../internal/utils"; | ||
import { MAX_64_BIG_INT } from "../internal/consts"; | ||
describe("object tests", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
}); | ||
const externalArgs = externalArgsApiToExternalArgsApi({}); | ||
test("delete object prop", () => { | ||
const objectBuffer = createObjectBuffer<any>(externalArgs, 1024, {}); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`864`); | ||
const sizeBeforeSet = memoryStats(objectBuffer).available; | ||
expect(sizeBeforeSet).toMatchInlineSnapshot(`864`); | ||
objectBuffer.foo = "a"; | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`776`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`744`); | ||
delete objectBuffer.foo; | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`864`); | ||
expect(memoryStats(objectBuffer).available).toBe(sizeBeforeSet); | ||
expect(objectBuffer).toMatchInlineSnapshot(`Object {}`); | ||
@@ -36,6 +33,8 @@ }); | ||
foo: { a: 1, b: true, c: false, d: null, e: undefined }, | ||
bigintPositive: MAX_64_BIG_INT, | ||
bigintNegative: MAX_64_BIG_INT * BigInt("-1"), | ||
}; | ||
const objectBuffer = createObjectBuffer<any>(externalArgs, 2048, input); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`1016`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`600`); | ||
expect(input).toEqual(objectBuffer); | ||
@@ -46,2 +45,4 @@ expect(objectBuffer).toMatchInlineSnapshot(` | ||
"b": true, | ||
"bigintNegative": -18446744073709551615n, | ||
"bigintPositive": 18446744073709551615n, | ||
"c": false, | ||
@@ -60,2 +61,28 @@ "d": null, | ||
}); | ||
// Not working. will make infinite loop | ||
test.skip("With circular", () => { | ||
const input: any = { | ||
a: 1, | ||
b: true, | ||
c: false, | ||
d: null, | ||
e: undefined, | ||
foo: { a: 1, b: true, c: false, d: null, e: undefined }, | ||
}; | ||
// Create circularity | ||
input.foo.circular = input.foo; | ||
const objectBuffer = createObjectBuffer<any>(externalArgs, 2048, input); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`1016`); | ||
expect(input).toEqual(objectBuffer); | ||
expect(objectBuffer.foo.circular).toEqual(objectBuffer.foo); | ||
expect(objectBuffer.foo.circular.d).toMatchInlineSnapshot(); | ||
objectBuffer.foo.circular = "severe the circularity"; | ||
expect(objectBuffer).toMatchInlineSnapshot(); | ||
}); | ||
}); |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer, resizeObjectBuffer } from "../"; | ||
@@ -18,4 +16,2 @@ import { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -40,3 +36,3 @@ }); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`776`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`760`); | ||
expect(objectBuffer).toMatchInlineSnapshot(` | ||
@@ -43,0 +39,0 @@ Object { |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -12,4 +10,2 @@ import { memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -35,3 +31,3 @@ }); | ||
expect(memoryStats(objectBuffer).available).toEqual(freeSpaceLeft); | ||
expect(freeSpaceLeft).toMatchInlineSnapshot(`40`); | ||
expect(freeSpaceLeft).toMatchInlineSnapshot(`24`); | ||
@@ -38,0 +34,0 @@ expect(objectBuffer).toMatchInlineSnapshot(` |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -10,6 +8,3 @@ import { memoryStats } from "../internal/api"; | ||
describe("Set tests", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
}); | ||
const externalArgs = externalArgsApiToExternalArgsApi({}); | ||
@@ -21,3 +16,3 @@ test("creation", () => { | ||
objectBuffer.foo = new Set(["a"]); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`616`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`584`); | ||
expect(objectBuffer.foo).toMatchInlineSnapshot(` | ||
@@ -36,3 +31,3 @@ Set { | ||
objectBuffer.foo.add("b"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`552`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
expect(objectBuffer.foo).toMatchInlineSnapshot(` | ||
@@ -60,7 +55,7 @@ Set { | ||
objectBuffer.foo.add("b"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`552`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
objectBuffer.foo.delete(1); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`552`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
@@ -81,7 +76,7 @@ expect(objectBuffer.foo).toMatchInlineSnapshot(` | ||
objectBuffer.foo.add("b"); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`552`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
objectBuffer.foo.clear(); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`680`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`664`); | ||
@@ -98,3 +93,3 @@ expect(objectBuffer.foo).toMatchInlineSnapshot(`Set {}`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`552`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`504`); | ||
@@ -101,0 +96,0 @@ expect(objectBuffer.foo).toMatchInlineSnapshot(` |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from "../"; | ||
@@ -12,4 +10,2 @@ import { memoryStats, disposeWrapperObject } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -26,2 +22,15 @@ }); | ||
// expect(objectBuffer).toMatchInlineSnapshot(` | ||
// Object { | ||
// "arr": Array [ | ||
// Object { | ||
// "a": 1, | ||
// }, | ||
// ], | ||
// } | ||
// `); | ||
// disposeWrapperObject(objectBuffer.arr[0]); | ||
// disposeWrapperObject(objectBuffer.arr); | ||
objectBuffer.arr = [{ bar: 666 }]; | ||
@@ -39,3 +48,9 @@ | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`480`); | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`512`); | ||
disposeWrapperObject(objectBuffer.arr[0]); | ||
disposeWrapperObject(objectBuffer.arr); | ||
delete objectBuffer.arr; | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`160`); | ||
}); | ||
@@ -89,4 +104,4 @@ | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`664`); | ||
expect(memoryStats(objectBuffer).used).toMatchInlineSnapshot(`712`); | ||
}); | ||
}); |
/* eslint-disable */ | ||
import { ExternalArgs } from "../internal/interfaces"; | ||
import * as util from "util"; | ||
import { createObjectBuffer } from ".."; | ||
@@ -27,6 +27,6 @@ import { memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
}); | ||
@@ -33,0 +33,0 @@ |
@@ -1,2 +0,1 @@ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from ".."; | ||
@@ -10,4 +9,2 @@ import { memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -23,3 +20,3 @@ }); | ||
expect(initialFreeSpace).toMatchInlineSnapshot(`32`); | ||
expect(initialFreeSpace).toMatchInlineSnapshot(`16`); | ||
@@ -34,3 +31,3 @@ expect(() => { | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`32`); | ||
expect(memoryStats(objectBuffer).available).toMatchInlineSnapshot(`16`); | ||
@@ -37,0 +34,0 @@ expect(objectBuffer).toMatchInlineSnapshot(` |
@@ -1,2 +0,1 @@ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from ".."; | ||
@@ -10,4 +9,2 @@ import { memoryStats } from "../internal/api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -14,0 +11,0 @@ }); |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
@@ -14,4 +13,2 @@ import { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -41,4 +38,2 @@ }); | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -63,4 +58,2 @@ }); | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 0, | ||
@@ -67,0 +60,0 @@ }); |
@@ -16,3 +16,2 @@ /** | ||
replaceUnderlyingArrayBuffer, | ||
// eslint-disable-next-line @typescript-eslint/camelcase | ||
sizeOf as unreliable_sizeOf, | ||
@@ -19,0 +18,0 @@ memoryStats, |
@@ -5,3 +5,6 @@ import { IMemPool } from "@thi.ng/malloc"; | ||
// extend pool and not monkey patch? need to think about it | ||
export function allocationsTransaction(operation: () => void, pool: IMemPool) { | ||
export function allocationsTransaction<T>( | ||
operation: () => T, | ||
pool: IMemPool | ||
): T { | ||
const allocations: number[] = []; | ||
@@ -62,7 +65,9 @@ const originalMalloc = pool.malloc; | ||
operation(); | ||
pool.malloc = originalMalloc; | ||
pool.calloc = originalCalloc; | ||
pool.realloc = originalRealloc; | ||
try { | ||
return operation(); | ||
} finally { | ||
pool.malloc = originalMalloc; | ||
pool.calloc = originalCalloc; | ||
pool.realloc = originalRealloc; | ||
} | ||
} |
import { initializeArrayBuffer } from "./store"; | ||
import { objectSaver } from "./objectSaver"; | ||
import { createObjectWrapper } from "./objectWrapper"; | ||
@@ -9,3 +8,3 @@ import { ExternalArgsApi, GlobalCarrier } from "./interfaces"; | ||
getInternalAPI, | ||
isPrimitive, | ||
isSupportedTopLevelValue, | ||
} from "./utils"; | ||
@@ -16,2 +15,5 @@ import { getCacheFor } from "./externalObjectsCache"; | ||
import { UnsupportedOperationError } from "./exceptions"; | ||
import { createHeap } from "../structsGenerator/consts"; | ||
import { saveValueIterative } from "./saveValue"; | ||
import { allocationsTransaction } from "./allocationsTransaction"; | ||
@@ -39,9 +41,3 @@ export interface CreateObjectBufferOptions { | ||
): T { | ||
if ( | ||
Array.isArray(initialValue) || | ||
initialValue instanceof Date || | ||
initialValue instanceof Map || | ||
initialValue instanceof Set || | ||
isPrimitive(initialValue) | ||
) { | ||
if (!isSupportedTopLevelValue(initialValue)) { | ||
throw new UnsupportedOperationError(); | ||
@@ -62,26 +58,22 @@ } | ||
const carrier: GlobalCarrier = { | ||
// dataView, | ||
allocator, | ||
uint8: new Uint8Array(arrayBuffer), | ||
uint16: new Uint16Array(arrayBuffer), | ||
uint32: new Uint32Array(arrayBuffer), | ||
float64: new Float64Array(arrayBuffer), | ||
bigUint64: new BigUint64Array(arrayBuffer), | ||
heap: createHeap(arrayBuffer), | ||
}; | ||
const start = objectSaver( | ||
externalArgsApiToExternalArgsApi(externalArgs), | ||
carrier, | ||
[], | ||
initialValue | ||
); | ||
allocationsTransaction(() => { | ||
saveValueIterative( | ||
externalArgsApiToExternalArgsApi(externalArgs), | ||
carrier, | ||
[], | ||
INITIAL_ENTRY_POINTER_TO_POINTER, | ||
initialValue | ||
); | ||
}, allocator); | ||
carrier.uint32[ | ||
INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT | ||
] = start; | ||
return createObjectWrapper( | ||
externalArgsApiToExternalArgsApi(externalArgs), | ||
carrier, | ||
start | ||
carrier.heap.Uint32Array[ | ||
INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT | ||
] | ||
); | ||
@@ -96,3 +88,3 @@ } | ||
*/ | ||
export function resizeObjectBuffer(objectBuffer: any, newSize: number) { | ||
export function resizeObjectBuffer(objectBuffer: unknown, newSize: number) { | ||
const oldArrayBuffer = getUnderlyingArrayBuffer(objectBuffer); | ||
@@ -115,5 +107,5 @@ const newArrayBuffer = new ArrayBuffer(newSize); | ||
export function getUnderlyingArrayBuffer( | ||
objectBuffer: any | ||
objectBuffer: unknown | ||
): ArrayBuffer | SharedArrayBuffer { | ||
return getInternalAPI(objectBuffer).getCarrier().uint8.buffer; | ||
return getInternalAPI(objectBuffer).getCarrier().heap.Uint8Array.buffer; | ||
} | ||
@@ -134,3 +126,2 @@ | ||
): T { | ||
// const dataView = new DataView(arrayBuffer); | ||
const allocator = new MemPool({ | ||
@@ -144,9 +135,4 @@ align: 8, | ||
const carrier: GlobalCarrier = { | ||
// dataView, | ||
allocator, | ||
uint8: new Uint8Array(arrayBuffer), | ||
uint16: new Uint16Array(arrayBuffer), | ||
uint32: new Uint32Array(arrayBuffer), | ||
float64: new Float64Array(arrayBuffer), | ||
bigUint64: new BigUint64Array(arrayBuffer), | ||
heap: createHeap(arrayBuffer), | ||
}; | ||
@@ -157,3 +143,3 @@ | ||
carrier, | ||
carrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
INITIAL_ENTRY_POINTER_TO_POINTER / Uint32Array.BYTES_PER_ELEMENT | ||
@@ -175,3 +161,3 @@ ] | ||
export function replaceUnderlyingArrayBuffer( | ||
objectBuffer: any, | ||
objectBuffer: unknown, | ||
newArrayBuffer: ArrayBuffer | SharedArrayBuffer | ||
@@ -197,13 +183,8 @@ ) { | ||
const carrier: GlobalCarrier = { | ||
// dataView: new DataView(newArrayBuffer), | ||
allocator, | ||
uint8: new Uint8Array(newArrayBuffer), | ||
uint16: new Uint16Array(newArrayBuffer), | ||
uint32: new Uint32Array(newArrayBuffer), | ||
float64: new Float64Array(newArrayBuffer), | ||
bigUint64: new BigUint64Array(newArrayBuffer), | ||
heap: createHeap(newArrayBuffer), | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
allocator.end = newArrayBuffer.byteLength; | ||
@@ -219,3 +200,3 @@ | ||
*/ | ||
export function memoryStats(objectBuffer: any) { | ||
export function memoryStats(objectBuffer: unknown) { | ||
const buf = getUnderlyingArrayBuffer(objectBuffer); | ||
@@ -241,3 +222,3 @@ | ||
export function updateExternalArgs( | ||
objectBuffer: any, | ||
objectBuffer: unknown, | ||
options: Partial<ExternalArgsApi> | ||
@@ -244,0 +225,0 @@ ) { |
@@ -1,20 +0,16 @@ | ||
import { | ||
readEntry, | ||
writeEntry, | ||
writeValueInPtrToPtrAndHandleMemory, | ||
} from "./store"; | ||
import { ArrayEntry, ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { writeValueInPtrToPtrAndHandleMemory } from "./store"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { assertNonNull } from "./assertNonNull"; | ||
import { | ||
array_length_get, | ||
array_dataspacePointer_get, | ||
array_allocatedLength_get, | ||
array_length_set, | ||
array_set_all, | ||
array_refsCount_get, | ||
} from "./generatedStructs"; | ||
import { Heap } from "../structsGenerator/consts"; | ||
export function arrayGetMetadata( | ||
carrier: GlobalCarrier, | ||
pointerToArrayEntry: number | ||
) { | ||
const arrayEntry = readEntry(carrier, pointerToArrayEntry) as ArrayEntry; | ||
return arrayEntry; | ||
} | ||
export function arrayGetPointersToValueInIndex( | ||
@@ -25,6 +21,4 @@ carrier: GlobalCarrier, | ||
) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
// out of bound | ||
if (indexToGet >= metadata.length) { | ||
if (indexToGet >= array_length_get(carrier.heap, pointerToArrayEntry)) { | ||
return undefined; | ||
@@ -34,7 +28,11 @@ } | ||
const pointerToThePointer = | ||
metadata.value + indexToGet * Uint32Array.BYTES_PER_ELEMENT; | ||
array_dataspacePointer_get(carrier.heap, pointerToArrayEntry) + | ||
indexToGet * Uint32Array.BYTES_PER_ELEMENT; | ||
const pointer = | ||
carrier.uint32[pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
carrier.heap.Uint32Array[ | ||
pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT | ||
]; | ||
// @todo avoid intermediate object | ||
return { | ||
@@ -82,3 +80,3 @@ pointer, | ||
assertNonNull(pointers); | ||
carrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
pointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT | ||
@@ -119,8 +117,8 @@ ] = pointerToEntry; | ||
) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
if (wishedLength > metadata.length) { | ||
if (wishedLength > metadata.allocatedLength) { | ||
if (wishedLength > array_length_get(carrier.heap, pointerToArrayEntry)) { | ||
if ( | ||
wishedLength > | ||
array_allocatedLength_get(carrier.heap, pointerToArrayEntry) | ||
) { | ||
reallocateArray( | ||
externalArgs, | ||
carrier, | ||
@@ -133,9 +131,3 @@ pointerToArrayEntry, | ||
// no need to re-allocated, just push the length forward | ||
writeEntry(carrier, pointerToArrayEntry, { | ||
type: ENTRY_TYPE.ARRAY, | ||
refsCount: metadata.refsCount, | ||
value: metadata.value, | ||
allocatedLength: metadata.allocatedLength, | ||
length: wishedLength, | ||
}); | ||
array_length_set(carrier.heap, pointerToArrayEntry, wishedLength); | ||
} | ||
@@ -149,20 +141,10 @@ } | ||
export function shrinkArray( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
pointerToArrayEntry: number, | ||
wishedLength: number | ||
) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
writeEntry(carrier, pointerToArrayEntry, { | ||
type: ENTRY_TYPE.ARRAY, | ||
refsCount: metadata.refsCount, | ||
value: metadata.value, | ||
allocatedLength: metadata.allocatedLength, | ||
length: wishedLength, | ||
}); | ||
array_length_set(heap, pointerToArrayEntry, wishedLength); | ||
} | ||
function reallocateArray( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
@@ -172,30 +154,28 @@ pointerToArrayEntry: number, | ||
newLength: number | ||
) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
const newArrayValueLocation = carrier.allocator.realloc( | ||
metadata.value, | ||
newAllocatedLength * Uint32Array.BYTES_PER_ELEMENT | ||
): void { | ||
const dataspacePointer = array_dataspacePointer_get( | ||
carrier.heap, | ||
pointerToArrayEntry | ||
); | ||
// for ( | ||
// let memoryToCopyIndex = 0; | ||
// memoryToCopyIndex < metadata.length; | ||
// memoryToCopyIndex += 1 | ||
// ) { | ||
// carrier.dataView.setUint32( | ||
// newArrayValueLocation + memoryToCopyIndex * Uint32Array.BYTES_PER_ELEMENT, | ||
// carrier.dataView.getUint32( | ||
// metadata.value + memoryToCopyIndex * Uint32Array.BYTES_PER_ELEMENT | ||
// ) | ||
// ); | ||
// } | ||
// if array was zero length, dataspacePointer was zero | ||
const newArrayValueLocation = | ||
dataspacePointer !== 0 | ||
? carrier.allocator.realloc( | ||
dataspacePointer, | ||
newAllocatedLength * Uint32Array.BYTES_PER_ELEMENT | ||
) | ||
: carrier.allocator.calloc( | ||
newAllocatedLength * Uint32Array.BYTES_PER_ELEMENT | ||
); | ||
writeEntry(carrier, pointerToArrayEntry, { | ||
type: ENTRY_TYPE.ARRAY, | ||
refsCount: metadata.refsCount, | ||
value: newArrayValueLocation, | ||
allocatedLength: newAllocatedLength, | ||
length: newLength, | ||
}); | ||
array_set_all( | ||
carrier.heap, | ||
pointerToArrayEntry, | ||
ENTRY_TYPE.ARRAY, | ||
array_refsCount_get(carrier.heap, pointerToArrayEntry), | ||
newArrayValueLocation, | ||
newLength, | ||
newAllocatedLength | ||
); | ||
} | ||
@@ -205,12 +185,19 @@ | ||
externalArgs: ExternalArgs, | ||
globalCarrier: GlobalCarrier, | ||
carrier: GlobalCarrier, | ||
pointerToArrayEntry: number, | ||
sortComparator: (a: any, b: any) => 1 | -1 | 0 = defaultCompareFunction | ||
) { | ||
const metadata = arrayGetMetadata(globalCarrier, pointerToArrayEntry); | ||
const pointersToValues = [...new Array(metadata.length).keys()] | ||
.map((index) => metadata.value + index * Uint32Array.BYTES_PER_ELEMENT) | ||
const arrayDataSpace = array_dataspacePointer_get( | ||
carrier.heap, | ||
pointerToArrayEntry | ||
); | ||
const pointersToValues = [ | ||
...new Array(array_length_get(carrier.heap, pointerToArrayEntry)).keys(), | ||
] | ||
.map((index) => arrayDataSpace + index * Uint32Array.BYTES_PER_ELEMENT) | ||
.map( | ||
(pointerToPointer) => | ||
globalCarrier.uint32[pointerToPointer / Uint32Array.BYTES_PER_ELEMENT] | ||
carrier.heap.Uint32Array[ | ||
pointerToPointer / Uint32Array.BYTES_PER_ELEMENT | ||
] | ||
); | ||
@@ -221,3 +208,3 @@ | ||
pointer, | ||
entryToFinalJavaScriptValue(externalArgs, globalCarrier, pointer), | ||
entryToFinalJavaScriptValue(externalArgs, carrier, pointer), | ||
] as const; | ||
@@ -231,4 +218,4 @@ }); | ||
for (let i = 0; i < sortMe.length; i += 1) { | ||
globalCarrier.uint32[ | ||
(metadata.value + i * Uint32Array.BYTES_PER_ELEMENT) / | ||
carrier.heap.Uint32Array[ | ||
(arrayDataSpace + i * Uint32Array.BYTES_PER_ELEMENT) / | ||
Uint32Array.BYTES_PER_ELEMENT | ||
@@ -277,11 +264,13 @@ ] = sortMe[i][0]; | ||
export function arrayReverse( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
pointerToArrayEntry: number | ||
) { | ||
const metadata = arrayGetMetadata(carrier, pointerToArrayEntry); | ||
for ( | ||
let i = 0; | ||
i < Math.floor(array_length_get(carrier.heap, pointerToArrayEntry) / 2); | ||
i += 1 | ||
) { | ||
const theOtherIndex = | ||
array_length_get(carrier.heap, pointerToArrayEntry) - i - 1; | ||
for (let i = 0; i < Math.floor(metadata.length / 2); i += 1) { | ||
const theOtherIndex = metadata.length - i - 1; | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
@@ -288,0 +277,0 @@ const a = arrayGetPointersToValueInIndex(carrier, pointerToArrayEntry, i)!; |
/* eslint-env jest */ | ||
import { initializeArrayBuffer } from "./store"; | ||
import * as util from "util"; | ||
import { createArrayWrapper } from "./arrayWrapper"; | ||
import { arraySaver } from "./arraySaver"; | ||
import { MemPool } from "@thi.ng/malloc"; | ||
import { MEM_POOL_START } from "./consts"; | ||
import { externalArgsApiToExternalArgsApi } from "./utils"; | ||
import { makeCarrier } from "./testUtils"; | ||
import { createObjectBuffer } from ".."; | ||
import { memoryStats } from "./api"; | ||
describe("arraySplice tests", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 20, | ||
}); | ||
test("arrayWrapper splice - add + delete - array stay in same length", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const removed = arrayWrapper.splice(2, 3, "a", "b", "c"); | ||
@@ -88,25 +65,12 @@ const removedFromPlain = plainJSArray.splice(2, 3, "a", "b", "c"); | ||
expect(removedFromPlain).toEqual([...removed]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`0`); | ||
}); | ||
test("arrayWrapper splice - Just delete items from the middle", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper.splice(2, 3); | ||
@@ -139,25 +103,12 @@ plainJSArray.splice(2, 3); | ||
expect(plainJSArray).toEqual([...arrayWrapper]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`72`); | ||
}); | ||
test("arrayWrapper splice - Just add items in the middle", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper.splice(4, 0, "a", "b"); | ||
@@ -200,26 +151,12 @@ plainJSArray.splice(4, 0, "a", "b"); | ||
expect(plainJSArray).toEqual([...arrayWrapper]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper splice - add + delete - array will get longer", () => { | ||
const arrayBuffer = new ArrayBuffer(1024); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 3 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const availableCheckpoint = allocator.stats().available; | ||
const removed = arrayWrapper.splice(2, 2, "a", "b", "c", "d"); | ||
@@ -279,29 +216,14 @@ const removedFromPlain = plainJSArray.splice(2, 2, "a", "b", "c", "d"); | ||
expect(removedFromPlain).toEqual([...removed]); | ||
expect( | ||
availableCheckpoint - allocator.stats().available | ||
).toMatchInlineSnapshot(`96`); | ||
}); | ||
test("arrayWrapper splice - add + delete - array will get shorter", () => { | ||
const arrayBuffer = new ArrayBuffer(1024); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const availableCheckpoint = memoryStats(ob).available; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = ob.arr; | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const availableCheckpoint = allocator.stats().available; | ||
const removed = arrayWrapper.splice(2, 6, "a", "b", "c", "d"); | ||
@@ -364,25 +286,14 @@ const removedFromPlain = plainJSArray.splice(2, 6, "a", "b", "c", "d"); | ||
expect( | ||
availableCheckpoint - allocator.stats().available | ||
).toMatchInlineSnapshot(`96`); | ||
availableCheckpoint - memoryStats(ob).available | ||
).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper splice - start bigger than array", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const removed = arrayWrapper.splice(12, 3, "a", "b"); | ||
@@ -433,25 +344,12 @@ const removedFromPlain = plainJSArray.splice(12, 3, "a", "b"); | ||
expect(removedFromPlain).toEqual([...removed]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper splice - delete bigger than array", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const removed = arrayWrapper.splice(2, 20, "a", "b"); | ||
@@ -508,25 +406,12 @@ const removedFromPlain = plainJSArray.splice(2, 20, "a", "b"); | ||
expect(removedFromPlain).toEqual([...removed]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper splice - negative start", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 20 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const removed = arrayWrapper.splice(-4, 1, "a", "b"); | ||
@@ -583,25 +468,12 @@ const removedFromPlain = plainJSArray.splice(-4, 1, "a", "b"); | ||
expect(removedFromPlain).toEqual([...removed]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper splice - negative delete", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
const plainJSArray: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const ob = createObjectBuffer({ arrayAdditionalAllocation: 3 }, 1024, { | ||
arr: plainJSArray, | ||
}); | ||
const plainJSArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
const arrayWrapper = ob.arr; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], plainJSArray); | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
const removed = arrayWrapper.splice(4, -1, 50, 51); | ||
@@ -652,5 +524,3 @@ const removedFromPlain = plainJSArray.splice(4, -1, 50, 51); | ||
expect(removedFromPlain).toEqual([...removed]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`24`); | ||
}); | ||
}); |
import { | ||
arrayGetMetadata, | ||
getFinalValueAtArrayIndex, | ||
@@ -11,8 +10,13 @@ shrinkArray, | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { writeValueInPtrToPtr } from "./store"; | ||
import { writeValueInPtrToPtr, handleArcForDeletedValuePointer } from "./store"; | ||
import { array_length_get } from "./generatedStructs"; | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice#Syntax | ||
/** | ||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice#Syntax | ||
this function is not OOM same yet unfortunately, | ||
There's allocations after destructive mutations | ||
*/ | ||
export function arraySplice( | ||
externalArgs: ExternalArgs, | ||
globalCarrier: GlobalCarrier, | ||
carrier: GlobalCarrier, | ||
pointerToArrayEntry: number, | ||
@@ -23,8 +27,8 @@ startArg: number, | ||
) { | ||
const metadata = arrayGetMetadata(globalCarrier, pointerToArrayEntry); | ||
const arrayLength = array_length_get(carrier.heap, pointerToArrayEntry); | ||
const calcedStart = calculateSpliceStart(metadata.length, startArg); | ||
const calcedStart = calculateSpliceStart(arrayLength, startArg); | ||
const calcedDeleteCount = calculateDeleteCount( | ||
metadata.length, | ||
arrayLength, | ||
calcedStart, | ||
@@ -34,14 +38,9 @@ deleteCountArg | ||
const newLength = metadata.length + itemsToAddArg.length - calcedDeleteCount; | ||
const newLength = arrayLength + itemsToAddArg.length - calcedDeleteCount; | ||
extendArrayIfNeeded( | ||
externalArgs, | ||
globalCarrier, | ||
pointerToArrayEntry, | ||
newLength | ||
); | ||
extendArrayIfNeeded(externalArgs, carrier, pointerToArrayEntry, newLength); | ||
const deletedItemsToReturn = []; | ||
// can be negative | ||
const itemCountChange = newLength - metadata.length; | ||
const itemCountChange = newLength - arrayLength; | ||
@@ -56,3 +55,3 @@ for ( | ||
externalArgs, | ||
globalCarrier, | ||
carrier, | ||
pointerToArrayEntry, | ||
@@ -62,2 +61,11 @@ deletedItemIndexToSave | ||
); | ||
handleArcForDeletedValuePointer( | ||
carrier, | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
arrayGetPointersToValueInIndex( | ||
carrier, | ||
pointerToArrayEntry, | ||
deletedItemIndexToSave | ||
)!.pointer | ||
); | ||
} | ||
@@ -78,3 +86,3 @@ | ||
const valueToCopyPointers = arrayGetPointersToValueInIndex( | ||
globalCarrier, | ||
carrier, | ||
pointerToArrayEntry, | ||
@@ -87,3 +95,3 @@ writeValueToIndex - itemCountChange | ||
setValuePointerAtArrayIndex( | ||
globalCarrier, | ||
carrier, | ||
pointerToArrayEntry, | ||
@@ -94,3 +102,3 @@ writeValueToIndex, | ||
globalCarrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
valueToCopyPointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT | ||
@@ -109,15 +117,14 @@ ] = 0; | ||
let writeValueToIndex = calcedStart + itemsToAddArg.length; | ||
writeValueToIndex < metadata.length + itemCountChange; | ||
writeValueToIndex < arrayLength + itemCountChange; | ||
writeValueToIndex += 1 | ||
) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const valueToCopyPointers = arrayGetPointersToValueInIndex( | ||
globalCarrier, | ||
carrier, | ||
pointerToArrayEntry, | ||
writeValueToIndex - itemCountChange | ||
); | ||
)!; | ||
assertNonNull(valueToCopyPointers); | ||
setValuePointerAtArrayIndex( | ||
globalCarrier, | ||
carrier, | ||
pointerToArrayEntry, | ||
@@ -127,19 +134,2 @@ writeValueToIndex, | ||
); | ||
// empty old array index, its still allocated! | ||
globalCarrier.uint32[ | ||
valueToCopyPointers.pointerToThePointer / Uint32Array.BYTES_PER_ELEMENT | ||
] = 0; | ||
// using that is wastefull | ||
// setValueAtArrayIndex( | ||
// dataView, | ||
// textDecoder, | ||
// textEncoder, | ||
// arrayAdditionalAllocation, | ||
// pointerToArrayEntry, | ||
// writeValueToIndex + calcedDeleteCount, | ||
// undefined | ||
// ); | ||
} | ||
@@ -150,3 +140,3 @@ } | ||
const valueToSetPointers = arrayGetPointersToValueInIndex( | ||
globalCarrier, | ||
carrier, | ||
pointerToArrayEntry, | ||
@@ -160,3 +150,3 @@ calcedStart + i | ||
externalArgs, | ||
globalCarrier, | ||
carrier, | ||
valueToSetPointers.pointerToThePointer, | ||
@@ -167,4 +157,4 @@ itemsToAddArg[i] | ||
if (newLength < metadata.length) { | ||
shrinkArray(externalArgs, globalCarrier, pointerToArrayEntry, newLength); | ||
if (newLength < arrayLength) { | ||
shrinkArray(carrier.heap, pointerToArrayEntry, newLength); | ||
} | ||
@@ -189,3 +179,3 @@ | ||
if (deleteCountArg === undefined || deleteCountArg >= arrayLength - start) { | ||
return arrayLength - start; | ||
return Math.min(arrayLength, arrayLength - start); | ||
} | ||
@@ -197,3 +187,3 @@ | ||
return deleteCountArg; | ||
return Math.min(arrayLength, deleteCountArg); | ||
} |
/* eslint-env jest */ | ||
import { initializeArrayBuffer } from "./store"; | ||
import * as util from "util"; | ||
import { createArrayWrapper } from "./arrayWrapper"; | ||
import { arraySaver } from "./arraySaver"; | ||
import { MemPool } from "@thi.ng/malloc"; | ||
import { MEM_POOL_START } from "./consts"; | ||
import { externalArgsApiToExternalArgsApi } from "./utils"; | ||
import { makeCarrier } from "./testUtils"; | ||
import { createObjectBuffer, memoryStats } from "./api"; | ||
describe("arrayWrapper tests", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 20, | ||
}); | ||
describe("arrayWrapper - general", () => { | ||
test("arrayWrapper class 1", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 20 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper: any = createArrayWrapper( | ||
externalArgs, | ||
carrier, | ||
saverOutput | ||
); | ||
expect(arrayWrapper).toMatchInlineSnapshot(` | ||
@@ -47,46 +24,28 @@ Array [ | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`264`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper array.keys()", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 20 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
externalArgs, | ||
carrier, | ||
saverOutput | ||
); | ||
expect([...arrayWrapper.keys()]).toEqual([0, 1, 2]); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`264`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper array.entries()", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 20 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
externalArgs, | ||
carrier, | ||
saverOutput | ||
); | ||
expect([...arrayWrapper.entries()]).toMatchInlineSnapshot(` | ||
@@ -109,23 +68,14 @@ Array [ | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`264`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper array.values() & iterator", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 20 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
externalArgs, | ||
carrier, | ||
saverOutput | ||
); | ||
expect([...arrayWrapper.values()]).toMatchInlineSnapshot(` | ||
@@ -146,19 +96,14 @@ Array [ | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`264`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`24`); | ||
}); | ||
test("arrayWrapper set value in bound", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 20 }, | ||
1024, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper: any = createArrayWrapper( | ||
externalArgs, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper[1] = "new value"; | ||
@@ -176,20 +121,10 @@ | ||
test("arrayWrapper set value out of bound, but inside allocated space", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver( | ||
{ ...externalArgs, arrayAdditionalAllocation: 15 }, | ||
carrier, | ||
[], | ||
arrayToSave | ||
); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 15 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper: any = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 15 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper[10] = "new value"; | ||
@@ -215,19 +150,10 @@ | ||
test("arrayWrapper set value out of bound, but outside allocated space", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = ["a", "b", 1]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 3 }, | ||
1024, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper: any = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper[10] = "new value"; | ||
@@ -250,3 +176,3 @@ | ||
`); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`232`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`496`); | ||
}); | ||
@@ -256,20 +182,10 @@ }); | ||
test("arrayWrapper sort - no comparator", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = [2, 1, null, 3, 10, undefined, 6, 77]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 3 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper.sort(); | ||
@@ -290,14 +206,6 @@ | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`176`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`32`); | ||
}); | ||
test("arrayWrapper sort - with comparator", () => { | ||
const arrayBuffer = new ArrayBuffer(2048); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = [2, 1, 3, 10, 6, 77].map((value) => ({ | ||
@@ -307,10 +215,8 @@ value, | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 3 }, | ||
1024 * 2, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper.sort((a, b) => { | ||
@@ -349,24 +255,14 @@ if (a.value > b.value) { | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`568`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`376`); | ||
}); | ||
test("arrayWrapper - reverse", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = [1, 2, 3, 4, 5, 6, 7]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 3 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper.reverse(); | ||
@@ -390,24 +286,14 @@ arrayWrapper.reverse(); | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`152`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`16`); | ||
}); | ||
test("arrayWrapper - set length", () => { | ||
const arrayBuffer = new ArrayBuffer(512); | ||
const carrier = makeCarrier(arrayBuffer); | ||
initializeArrayBuffer(arrayBuffer); | ||
const allocator = new MemPool({ | ||
buf: arrayBuffer, | ||
start: MEM_POOL_START, | ||
}); | ||
const arrayToSave = [1, 2, 3, 4, 5, 6, 7]; | ||
const saverOutput = arraySaver(externalArgs, carrier, [], arrayToSave); | ||
const arrayWrapper = createObjectBuffer( | ||
{ arrayAdditionalAllocation: 3 }, | ||
512, | ||
{ arrayToSave } | ||
).arrayToSave; | ||
const arrayWrapper = createArrayWrapper( | ||
{ ...externalArgs, arrayAdditionalAllocation: 3 }, | ||
carrier, | ||
saverOutput | ||
); | ||
arrayWrapper.length = 10; | ||
@@ -448,4 +334,4 @@ expect(arrayWrapper).toMatchInlineSnapshot(` | ||
expect(allocator.stats().available).toMatchInlineSnapshot(`152`); | ||
expect(memoryStats(arrayWrapper).available).toMatchInlineSnapshot(`160`); | ||
}); | ||
}); |
import { | ||
getFinalValueAtArrayIndex, | ||
arrayGetMetadata, | ||
setValueAtArrayIndex, | ||
@@ -11,3 +10,3 @@ arraySort, | ||
import { arraySplice } from "./arraySplice"; | ||
import { ExternalArgs, GlobalCarrier, ArrayEntry } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { | ||
@@ -19,6 +18,7 @@ IllegalArrayIndexError, | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
import { array_length_get } from "./generatedStructs"; | ||
export class ArrayWrapper extends BaseProxyTrap<ArrayEntry> | ||
implements ProxyHandler<{}> { | ||
public get(target: {}, p: PropertyKey): any { | ||
export class ArrayWrapper extends BaseProxyTrap | ||
implements ProxyHandler<Record<string, unknown>> { | ||
public get(target: Record<string, unknown>, p: PropertyKey): any { | ||
if (p === INTERNAL_API_SYMBOL) { | ||
@@ -29,4 +29,4 @@ return this; | ||
if (p in this && p !== "constructor") { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
return this[p]; | ||
@@ -36,3 +36,3 @@ } | ||
if (p === "length") { | ||
return arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
return array_length_get(this.carrier.heap, this.entryPointer); | ||
} | ||
@@ -53,8 +53,11 @@ | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
return target[p]; | ||
} | ||
public deleteProperty(target: {}, p: PropertyKey): boolean { | ||
public deleteProperty( | ||
target: Record<string, unknown>, | ||
p: PropertyKey | ||
): boolean { | ||
const index = typeof p === "number" ? p : Number.parseInt(p as string, 10); | ||
@@ -70,3 +73,3 @@ | ||
public ownKeys(): PropertyKey[] { | ||
const length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
const length = array_length_get(this.carrier.heap, this.entryPointer); | ||
@@ -76,3 +79,3 @@ return [...new Array(length).keys(), "length"]; | ||
public getOwnPropertyDescriptor(target: {}, prop: any) { | ||
public getOwnPropertyDescriptor(target: Record<string, unknown>, prop: any) { | ||
if (prop === "length") { | ||
@@ -89,3 +92,3 @@ return { configurable: false, enumerable: false, writable: true }; | ||
public has(target: {}, p: PropertyKey): boolean { | ||
public has(target: Record<string, unknown>, p: PropertyKey): boolean { | ||
if (p === INTERNAL_API_SYMBOL) { | ||
@@ -95,3 +98,3 @@ return true; | ||
const length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
const length = array_length_get(this.carrier.heap, this.entryPointer); | ||
@@ -107,3 +110,7 @@ if (typeof p === "number") { | ||
public set(target: {}, accessedProp: PropertyKey, value: any): boolean { | ||
public set( | ||
target: Record<string, unknown>, | ||
accessedProp: PropertyKey, | ||
value: any | ||
): boolean { | ||
if (typeof accessedProp === "symbol") { | ||
@@ -118,4 +125,6 @@ throw new IllegalArrayIndexError(); | ||
const currentLength = arrayGetMetadata(this.carrier, this.entryPointer) | ||
.length; | ||
const currentLength = array_length_get( | ||
this.carrier.heap, | ||
this.entryPointer | ||
); | ||
@@ -187,3 +196,3 @@ if (currentLength === value) { | ||
length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
length = array_length_get(this.carrier.heap, this.entryPointer); | ||
} while (index < length); | ||
@@ -201,3 +210,3 @@ } | ||
length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
length = array_length_get(this.carrier.heap, this.entryPointer); | ||
} while (index < length); | ||
@@ -220,3 +229,3 @@ } | ||
length = arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
length = array_length_get(this.carrier.heap, this.entryPointer); | ||
} while (index < length); | ||
@@ -234,14 +243,16 @@ } | ||
public splice(start: number, deleteCount?: number, ...items: any[]) { | ||
return arraySplice( | ||
this.externalArgs, | ||
this.carrier, | ||
this.entryPointer, | ||
start, | ||
deleteCount, | ||
...items | ||
); | ||
return allocationsTransaction(() => { | ||
return arraySplice( | ||
this.externalArgs, | ||
this.carrier, | ||
this.entryPointer, | ||
start, | ||
deleteCount, | ||
...items | ||
); | ||
}, this.carrier.allocator); | ||
} | ||
public reverse() { | ||
arrayReverse(this.externalArgs, this.carrier, this.entryPointer); | ||
arrayReverse(this.carrier, this.entryPointer); | ||
return this; | ||
@@ -263,9 +274,5 @@ } | ||
return arrayGetMetadata(this.carrier, this.entryPointer).length; | ||
return array_length_get(this.carrier.heap, this.entryPointer); | ||
} | ||
// public getDataView() { | ||
// return this.carrier.dataView; | ||
// } | ||
public getEntryPointer() { | ||
@@ -287,3 +294,3 @@ return this.entryPointer; | ||
public defineProperty(): // target: {}, | ||
public defineProperty(): // target: Record<string, unknown>, | ||
// p: PropertyKey, | ||
@@ -312,2 +319,4 @@ // attributes: PropertyDescriptor | ||
return new Proxy( | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
[], | ||
@@ -314,0 +323,0 @@ new ArrayWrapper(externalArgs, globalCarrier, entryPointer) |
@@ -1,17 +0,6 @@ | ||
import { | ||
ExternalArgs, | ||
GlobalCarrier, | ||
InternalAPI, | ||
DateEntry, | ||
ArrayEntry, | ||
ObjectEntry, | ||
MapEntry, | ||
SetEntry, | ||
} from "./interfaces"; | ||
import { incrementRefCount, decrementRefCount, readEntry } from "./store"; | ||
import { ExternalArgs, GlobalCarrier, InternalAPI } from "./interfaces"; | ||
import { incrementRefCount, decrementRefCount } from "./store"; | ||
import { WrapperDestroyed } from "./exceptions"; | ||
export class BaseProxyTrap< | ||
T extends ObjectEntry | DateEntry | ArrayEntry | MapEntry | SetEntry | ||
> implements InternalAPI { | ||
export abstract class BaseProxyTrap implements InternalAPI { | ||
constructor( | ||
@@ -22,11 +11,7 @@ protected externalArgs: ExternalArgs, | ||
) { | ||
incrementRefCount(this.externalArgs, this.carrier, this.entryPointer); | ||
incrementRefCount(this.carrier.heap, this.entryPointer); | ||
} | ||
public destroy() { | ||
const newRefCount = decrementRefCount( | ||
this.externalArgs, | ||
this.carrier, | ||
this.entryPointer | ||
); | ||
const newRefCount = decrementRefCount(this.carrier.heap, this.entryPointer); | ||
this._entryPointer = 0; | ||
@@ -60,6 +45,2 @@ | ||
} | ||
protected get entry(): T { | ||
return readEntry(this.carrier, this.entryPointer) as T; | ||
} | ||
} |
@@ -15,1 +15,2 @@ export const LOCK_OFFSET = 0; | ||
export const FALSE_KNOWN_ADDRESS = 3; | ||
export const MAX_64_BIG_INT = BigInt("0xFFFFFFFFFFFFFFFF"); |
@@ -1,4 +0,3 @@ | ||
import { ExternalArgs, GlobalCarrier, DateEntry } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { readEntry, writeEntry } from "./store"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
@@ -8,2 +7,7 @@ import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
import { BaseProxyTrap } from "./BaseProxyTrap"; | ||
import { | ||
date_set_all, | ||
date_refsCount_get, | ||
date_timestamp_get, | ||
} from "./generatedStructs"; | ||
@@ -62,4 +66,3 @@ const getFunctions: Array<keyof Date> = [ | ||
export class DateWrapper extends BaseProxyTrap<DateEntry> | ||
implements ProxyHandler<Date> { | ||
export class DateWrapper extends BaseProxyTrap implements ProxyHandler<Date> { | ||
private dateObjectForReuse: Date; | ||
@@ -109,13 +112,17 @@ private useMeToGiveNamesToFunctionsAndCacheThem: any; | ||
private updateDateObjectForReuse() { | ||
const entry = readEntry(this.carrier, this.entryPointer) as DateEntry; | ||
this.dateObjectForReuse.setTime(entry.value); | ||
this.dateObjectForReuse.setTime( | ||
date_timestamp_get(this.carrier.heap, this.entryPointer) | ||
); | ||
} | ||
private persistDateObject() { | ||
writeEntry(this.carrier, this.entryPointer, { | ||
type: ENTRY_TYPE.DATE, | ||
refsCount: this.entry.refsCount, | ||
value: this.dateObjectForReuse.getTime(), | ||
}); | ||
date_set_all( | ||
this.carrier.heap, | ||
this.entryPointer, | ||
ENTRY_TYPE.DATE, | ||
date_refsCount_get(this.carrier.heap, this.entryPointer), | ||
// padding | ||
0, | ||
this.dateObjectForReuse.getTime() | ||
); | ||
} | ||
@@ -122,0 +129,0 @@ |
@@ -1,2 +0,1 @@ | ||
import * as util from "util"; | ||
import { createObjectBuffer } from ".."; | ||
@@ -9,4 +8,2 @@ import { externalArgsApiToExternalArgsApi } from "./utils"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 20, | ||
@@ -13,0 +10,0 @@ }); |
@@ -20,3 +20,3 @@ import { getInternalAPI } from "./utils"; | ||
const addressesToFree = getAllLinkedAddresses( | ||
internalApi.getCarrier(), | ||
internalApi.getCarrier().heap, | ||
false, | ||
@@ -26,12 +26,10 @@ entryPointer | ||
const { allocator, heap } = internalApi.getCarrier(); | ||
for (const address of addressesToFree.leafAddresses) { | ||
internalApi.getCarrier().allocator.free(address); | ||
allocator.free(address); | ||
} | ||
for (const address of addressesToFree.arcAddresses) { | ||
decrementRefCount( | ||
internalApi.getExternalArgs(), | ||
internalApi.getCarrier(), | ||
address | ||
); | ||
decrementRefCount(heap, address); | ||
} | ||
@@ -38,0 +36,0 @@ |
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { ENTRY_TYPE, isPrimitiveEntryType } from "./entry-types"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { createObjectWrapper } from "./objectWrapper"; | ||
@@ -7,3 +7,3 @@ import { createArrayWrapper } from "./arrayWrapper"; | ||
import { getCacheFor } from "./externalObjectsCache"; | ||
import { decrementRefCount, readEntry } from "./store"; | ||
import { decrementRefCount } from "./store"; | ||
import { getAllLinkedAddresses } from "./getAllLinkedAddresses"; | ||
@@ -18,6 +18,9 @@ import { createMapWrapper } from "./mapWrapper"; | ||
} from "./consts"; | ||
import { | ||
typeOnly_type_get, | ||
number_value_get, | ||
bigint_value_get, | ||
} from "./generatedStructs"; | ||
import { readString } from "./readString"; | ||
// declare const FinalizationGroup: any; | ||
// declare const WeakRef: any; | ||
const TYPE_TO_FACTORY = { | ||
@@ -52,47 +55,57 @@ [ENTRY_TYPE.OBJECT]: createObjectWrapper, | ||
const valueEntry = readEntry(carrier, pointerToEntry); | ||
const entryType: ENTRY_TYPE = typeOnly_type_get(carrier.heap, pointerToEntry); | ||
if (isPrimitiveEntryType(valueEntry.type)) { | ||
return valueEntry.value; | ||
switch (entryType) { | ||
case ENTRY_TYPE.NUMBER: | ||
return number_value_get(carrier.heap, pointerToEntry); | ||
break; | ||
case ENTRY_TYPE.STRING: | ||
return readString(carrier.heap, pointerToEntry); | ||
break; | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
return bigint_value_get(carrier.heap, pointerToEntry); | ||
break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
return bigint_value_get(carrier.heap, pointerToEntry) * BigInt("-1"); | ||
break; | ||
} | ||
// this is an invariant | ||
if ( | ||
valueEntry.type === ENTRY_TYPE.OBJECT || | ||
valueEntry.type === ENTRY_TYPE.DATE || | ||
valueEntry.type === ENTRY_TYPE.ARRAY || | ||
valueEntry.type === ENTRY_TYPE.MAP || | ||
valueEntry.type === ENTRY_TYPE.SET | ||
!( | ||
entryType === ENTRY_TYPE.OBJECT || | ||
entryType === ENTRY_TYPE.DATE || | ||
entryType === ENTRY_TYPE.ARRAY || | ||
entryType === ENTRY_TYPE.MAP || | ||
entryType === ENTRY_TYPE.SET | ||
) | ||
) { | ||
const cache = getCacheFor(carrier, (key) => { | ||
finalizer(key, carrier, externalArgs); | ||
}); | ||
throw new Error("Nope Nope Nope"); | ||
} | ||
let ret = cache.get(pointerToEntry); | ||
const cache = getCacheFor(carrier, (key) => { | ||
finalizer(key, carrier); | ||
}); | ||
if (!ret) { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
ret = TYPE_TO_FACTORY[valueEntry.type]( | ||
externalArgs, | ||
carrier, | ||
pointerToEntry | ||
); | ||
cache.set(pointerToEntry, ret); | ||
} | ||
let ret = cache.get(pointerToEntry); | ||
return ret; | ||
if (!ret) { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
ret = TYPE_TO_FACTORY[entryType](externalArgs, carrier, pointerToEntry); | ||
cache.set(pointerToEntry, ret); | ||
} | ||
throw new Error("unsupported yet"); | ||
return ret; | ||
} | ||
function finalizer( | ||
memoryAddress: number, | ||
carrier: GlobalCarrier, | ||
externalArgs: ExternalArgs | ||
) { | ||
const newRefsCount = decrementRefCount(externalArgs, carrier, memoryAddress); | ||
/* istanbul ignore next */ | ||
function finalizer(memoryAddress: number, carrier: GlobalCarrier) { | ||
const newRefsCount = decrementRefCount(carrier.heap, memoryAddress); | ||
if (newRefsCount === 0) { | ||
const freeUs = getAllLinkedAddresses(carrier, false, memoryAddress); | ||
const freeUs = getAllLinkedAddresses(carrier.heap, false, memoryAddress); | ||
@@ -104,5 +117,5 @@ for (const address of freeUs.leafAddresses) { | ||
for (const address of freeUs.arcAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
decrementRefCount(carrier.heap, address); | ||
} | ||
} | ||
} |
import { WeakValueMap } from "./WeakValueMap"; | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
const externalObjectsCache = new WeakMap<object, Map<number, any>>(); | ||
@@ -9,2 +10,3 @@ | ||
export function getCacheFor( | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
obj: object, | ||
@@ -11,0 +13,0 @@ externalFinalizer?: (key: number) => void |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { MemPool } from "@thi.ng/malloc"; | ||
@@ -11,4 +10,2 @@ import { createObjectBuffer, memoryStats } from "./api"; | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 20, | ||
@@ -32,5 +29,2 @@ }); | ||
}); | ||
// const a = objectBuffer.nestedObject; | ||
// getInternalAPI(a).destroy(); | ||
// // a.toString(); | ||
@@ -43,3 +37,3 @@ const carrier = getInternalAPI(objectBuffer).getCarrier(); | ||
const linkedAddresses = getAllLinkedAddresses( | ||
carrier, | ||
carrier.heap, | ||
false, | ||
@@ -80,3 +74,3 @@ // allocatedAddresses[allocatedAddresses.length - 1] | ||
const linkedAddresses = getAllLinkedAddresses( | ||
carrier, | ||
carrier.heap, | ||
false, | ||
@@ -107,14 +101,11 @@ entryPointer | ||
// const a = objectBuffer.nestedObject; | ||
// getInternalAPI(a).destroy(); | ||
// // a.toString(); | ||
const carrier = getInternalAPI(objectBuffer).getCarrier(); | ||
const entryPointer = getInternalAPI(objectBuffer).getEntryPointer(); | ||
getInternalAPI(objectBuffer).destroy(); | ||
const linkedAddresses = getAllLinkedAddresses( | ||
carrier, | ||
carrier.heap, | ||
false, | ||
allocatedAddresses[allocatedAddresses.length - 1] | ||
entryPointer | ||
); | ||
@@ -125,55 +116,2 @@ | ||
); | ||
expect([...linkedAddresses.leafAddresses].slice().sort()) | ||
.toMatchInlineSnapshot(` | ||
Array [ | ||
1008, | ||
1032, | ||
1056, | ||
1072, | ||
1096, | ||
112, | ||
1120, | ||
1144, | ||
1160, | ||
1184, | ||
1216, | ||
128, | ||
144, | ||
168, | ||
200, | ||
216, | ||
240, | ||
280, | ||
296, | ||
312, | ||
336, | ||
360, | ||
376, | ||
400, | ||
424, | ||
448, | ||
464, | ||
48, | ||
488, | ||
512, | ||
528, | ||
560, | ||
584, | ||
616, | ||
632, | ||
656, | ||
680, | ||
704, | ||
72, | ||
728, | ||
744, | ||
848, | ||
880, | ||
912, | ||
936, | ||
976, | ||
992, | ||
] | ||
`); | ||
}); | ||
@@ -200,8 +138,9 @@ | ||
expect(memoryStats(objectBuffer)).toMatchInlineSnapshot(` | ||
Object { | ||
"available": 816, | ||
"used": 1232, | ||
} | ||
`); | ||
Object { | ||
"available": 656, | ||
"used": 1392, | ||
} | ||
`); | ||
const entryPointer = getInternalAPI(objectBuffer).getEntryPointer(); | ||
const carrier = getInternalAPI(objectBuffer).getCarrier(); | ||
@@ -212,5 +151,5 @@ | ||
const linkedAddresses = getAllLinkedAddresses( | ||
carrier, | ||
carrier.heap, | ||
false, | ||
allocatedAddresses[allocatedAddresses.length - 1] | ||
entryPointer | ||
); | ||
@@ -217,0 +156,0 @@ |
@@ -1,9 +0,16 @@ | ||
import { readEntry } from "./store"; | ||
import { GlobalCarrier } from "./interfaces"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { hashMapGetPointersToFree } from "./hashmap/hashmap"; | ||
import { isKnownAddressValuePointer } from "./utils"; | ||
import { | ||
typeOnly_type_get, | ||
string_charsPointer_get, | ||
typeAndRc_refsCount_get, | ||
object_pointerToHashMap_get, | ||
array_dataspacePointer_get, | ||
array_length_get, | ||
} from "./generatedStructs"; | ||
import { Heap } from "../structsGenerator/consts"; | ||
export function getAllLinkedAddresses( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
ignoreRefCount: boolean, | ||
@@ -28,3 +35,3 @@ entryPointer: number | ||
getAllLinkedAddressesStep( | ||
carrier, | ||
heap, | ||
ignoreRefCount, | ||
@@ -42,2 +49,3 @@ addressToProcess, | ||
// @todo avoid intermediate object | ||
return { leafAddresses, arcAddresses }; | ||
@@ -47,3 +55,3 @@ } | ||
function getAllLinkedAddressesStep( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
ignoreRefCount: boolean, | ||
@@ -63,7 +71,8 @@ entryPointer: number, | ||
const entry = readEntry(carrier, entryPointer); | ||
const entryType = typeOnly_type_get(heap, entryPointer); | ||
// to be used ONLY if the type has ref counter | ||
const refsCount = typeAndRc_refsCount_get(heap, entryPointer); | ||
switch (entry.type) { | ||
switch (entryType) { | ||
case ENTRY_TYPE.NUMBER: | ||
case ENTRY_TYPE.STRING: | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
@@ -74,10 +83,15 @@ case ENTRY_TYPE.BIGINT_POSITIVE: | ||
case ENTRY_TYPE.STRING: | ||
leafAddresses.add(string_charsPointer_get(heap, entryPointer)); | ||
leafAddresses.add(entryPointer); | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.MAP: | ||
case ENTRY_TYPE.SET: | ||
if (entry.refsCount <= 1 || ignoreRefCount) { | ||
if (refsCount <= 1 || ignoreRefCount) { | ||
leafAddresses.add(entryPointer); | ||
getObjectOrMapOrSetAddresses( | ||
carrier, | ||
entry.value, | ||
heap, | ||
object_pointerToHashMap_get(heap, entryPointer), | ||
leafAddresses, | ||
@@ -89,14 +103,14 @@ addressesToProcessQueue | ||
} | ||
break; | ||
case ENTRY_TYPE.ARRAY: | ||
if (entry.refsCount <= 1 || ignoreRefCount) { | ||
if (refsCount <= 1 || ignoreRefCount) { | ||
leafAddresses.add(entryPointer); | ||
leafAddresses.add(entry.value); | ||
for (let i = 0; i < entry.allocatedLength; i += 1) { | ||
leafAddresses.add(array_dataspacePointer_get(heap, entryPointer)); | ||
const arrayLength = array_length_get(heap, entryPointer); | ||
for (let i = 0; i < arrayLength; i += 1) { | ||
const valuePointer = | ||
carrier.uint32[ | ||
(entry.value + i * Uint32Array.BYTES_PER_ELEMENT) / | ||
heap.Uint32Array[ | ||
(array_dataspacePointer_get(heap, entryPointer) + | ||
i * Uint32Array.BYTES_PER_ELEMENT) / | ||
Uint32Array.BYTES_PER_ELEMENT | ||
@@ -113,3 +127,3 @@ ]; | ||
case ENTRY_TYPE.DATE: | ||
if (entry.refsCount <= 1 || ignoreRefCount) { | ||
if (refsCount <= 1 || ignoreRefCount) { | ||
leafAddresses.add(entryPointer); | ||
@@ -122,5 +136,3 @@ } else { | ||
default: | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
throw new Error(ENTRY_TYPE[entryType] + " Not implemented yet"); | ||
} | ||
@@ -130,3 +142,3 @@ } | ||
export function getObjectOrMapOrSetAddresses( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
internalHashmapPointer: number, | ||
@@ -137,3 +149,3 @@ leafAddresses: Set<number>, | ||
const { pointersToValuePointers, pointers } = hashMapGetPointersToFree( | ||
carrier, | ||
heap, | ||
internalHashmapPointer | ||
@@ -148,5 +160,5 @@ ); | ||
addressesToProcessQueue.push( | ||
carrier.uint32[pointer / Uint32Array.BYTES_PER_ELEMENT] | ||
heap.Uint32Array[pointer / Uint32Array.BYTES_PER_ELEMENT] | ||
); | ||
} | ||
} |
/* eslint-env jest */ | ||
import * as util from "util"; | ||
import { | ||
@@ -18,10 +18,8 @@ arrayBuffer2HexArray, | ||
} from "./hashmap"; | ||
import { GlobalCarrier, StringEntry } from "../interfaces"; | ||
import { readEntry } from "../store"; | ||
import { GlobalCarrier } from "../interfaces"; | ||
import { externalArgsApiToExternalArgsApi } from "../utils"; | ||
import { readString } from "../readString"; | ||
describe("hashmap", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 20, | ||
@@ -60,3 +58,3 @@ }); | ||
carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT] = 5; | ||
carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT] = 5; | ||
@@ -76,3 +74,3 @@ expect(arrayBuffer2HexArray(ab, true)).toMatchSnapshot("after insert"); | ||
carrier.uint32[pointer / Uint32Array.BYTES_PER_ELEMENT] = 6; | ||
carrier.heap.Uint32Array[pointer / Uint32Array.BYTES_PER_ELEMENT] = 6; | ||
@@ -89,3 +87,3 @@ expect(arrayBuffer2HexArray(ab, true)).toMatchSnapshot("after insert"); | ||
const foundValuePointer = hashMapValueLookup(carrier, mapPointer, key); | ||
const foundValuePointer = hashMapValueLookup(carrier.heap, mapPointer, key); | ||
@@ -107,3 +105,3 @@ expect(pointer).toBe(foundValuePointer); | ||
const foundValuePointer = hashMapValueLookup(carrier, mapPointer, key); | ||
const foundValuePointer = hashMapValueLookup(carrier.heap, mapPointer, key); | ||
expect(foundValuePointer).toBe(valuePointer); | ||
@@ -126,3 +124,3 @@ }); | ||
const foundValuePointer = hashMapValueLookup(carrier, mapPointer, key); | ||
const foundValuePointer = hashMapValueLookup(carrier.heap, mapPointer, key); | ||
@@ -147,3 +145,3 @@ expect(foundValuePointer).toBe(firstValuePointer); | ||
const foundValuePointer = hashMapValueLookup( | ||
carrier, | ||
carrier.heap, | ||
mapPointer, | ||
@@ -156,3 +154,3 @@ "Not a real key" | ||
test("hashMapLowLevelIterator", () => { | ||
setABSize(2048); | ||
setABSize(2048 * 2); | ||
const mapPointer = createHashMap(carrier); | ||
@@ -171,3 +169,3 @@ | ||
carrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
inserts[inserts.length - 1] / Uint32Array.BYTES_PER_ELEMENT | ||
@@ -185,3 +183,3 @@ ] = index; | ||
(iteratorToken = hashMapLowLevelIterator( | ||
carrier, | ||
carrier.heap, | ||
mapPointer, | ||
@@ -191,7 +189,6 @@ iteratorToken | ||
) { | ||
values.push(hashMapNodePointerToKeyValue(carrier, iteratorToken)); | ||
values.push(hashMapNodePointerToKeyValue(carrier.heap, iteratorToken)); | ||
} | ||
expect( | ||
values.map((v) => (readEntry(carrier, v.keyPointer) as StringEntry).value) | ||
).toMatchInlineSnapshot(` | ||
expect(values.map((v) => readString(carrier.heap, v.keyPointer))) | ||
.toMatchInlineSnapshot(` | ||
Array [ | ||
@@ -229,6 +226,7 @@ "a", | ||
test("hashMapSize", () => { | ||
setABSize(2048); | ||
const abSize = 2048 * 4; | ||
setABSize(abSize); | ||
const mapPointer = createHashMap(carrier); | ||
expect(hashMapSize(carrier, mapPointer)).toMatchInlineSnapshot(`0`); | ||
expect(hashMapSize(carrier.heap, mapPointer)).toMatchInlineSnapshot(`0`); | ||
const memAvailableAfterEachStep = [carrier.allocator.stats().available]; | ||
@@ -241,3 +239,3 @@ | ||
for (const [index, useThatAsKey] of input.entries()) { | ||
carrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
hashMapInsertUpdate(externalArgs, carrier, mapPointer, useThatAsKey) / | ||
@@ -250,3 +248,3 @@ Uint32Array.BYTES_PER_ELEMENT | ||
expect(hashMapSize(carrier, mapPointer)).toMatchInlineSnapshot(`26`); | ||
expect(hashMapSize(carrier.heap, mapPointer)).toMatchInlineSnapshot(`26`); | ||
@@ -256,41 +254,42 @@ hashMapDelete(carrier, mapPointer, "a"); | ||
hashMapDelete(carrier, mapPointer, "c"); | ||
expect(hashMapSize(carrier, mapPointer)).toMatchInlineSnapshot(`26`); | ||
expect(hashMapSize(carrier.heap, mapPointer)).toMatchInlineSnapshot(`26`); | ||
memAvailableAfterEachStep.push(carrier.allocator.stats().available); | ||
expect(memAvailableAfterEachStep).toMatchInlineSnapshot(` | ||
Array [ | ||
1904, | ||
1840, | ||
1776, | ||
1712, | ||
1648, | ||
1584, | ||
1520, | ||
1456, | ||
1352, | ||
1288, | ||
1224, | ||
1160, | ||
1096, | ||
1032, | ||
968, | ||
904, | ||
760, | ||
696, | ||
632, | ||
568, | ||
504, | ||
440, | ||
376, | ||
312, | ||
248, | ||
184, | ||
120, | ||
120, | ||
] | ||
`); | ||
expect(memAvailableAfterEachStep.map((a) => abSize - a)) | ||
.toMatchInlineSnapshot(` | ||
Array [ | ||
144, | ||
224, | ||
304, | ||
384, | ||
464, | ||
544, | ||
624, | ||
704, | ||
824, | ||
904, | ||
984, | ||
1064, | ||
1144, | ||
1224, | ||
1304, | ||
1384, | ||
1464, | ||
1544, | ||
1624, | ||
1704, | ||
1784, | ||
1864, | ||
2024, | ||
2112, | ||
2192, | ||
2272, | ||
2352, | ||
2352, | ||
] | ||
`); | ||
}); | ||
test("hashMapGetPointersToFree", () => { | ||
setABSize(1024); | ||
setABSize(1024 * 4); | ||
let hashmapPointer = 0; | ||
@@ -307,3 +306,3 @@ | ||
expect(carrier.allocator.stats().available).toMatchInlineSnapshot(`880`); | ||
// expect(carrier.allocator.stats().available).toMatchInlineSnapshot(`880`); | ||
@@ -313,3 +312,3 @@ let toAdd: undefined | string; | ||
while ((toAdd = inputCopy.pop()) !== undefined) { | ||
carrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
hashMapInsertUpdate(externalArgs, carrier, hashmapPointer, toAdd) / | ||
@@ -321,57 +320,4 @@ Uint32Array.BYTES_PER_ELEMENT | ||
const r = hashMapGetPointersToFree(carrier, hashmapPointer); | ||
const r = hashMapGetPointersToFree(carrier.heap, hashmapPointer); | ||
expect(r).toMatchInlineSnapshot(` | ||
Object { | ||
"pointers": Array [ | ||
48, | ||
664, | ||
120, | ||
136, | ||
200, | ||
264, | ||
328, | ||
392, | ||
456, | ||
520, | ||
584, | ||
648, | ||
752, | ||
816, | ||
152, | ||
176, | ||
216, | ||
240, | ||
280, | ||
304, | ||
344, | ||
368, | ||
408, | ||
432, | ||
472, | ||
496, | ||
536, | ||
560, | ||
600, | ||
624, | ||
72, | ||
96, | ||
768, | ||
792, | ||
], | ||
"pointersToValuePointers": Array [ | ||
152, | ||
216, | ||
280, | ||
344, | ||
408, | ||
472, | ||
536, | ||
600, | ||
72, | ||
768, | ||
], | ||
} | ||
`); | ||
expect(r.pointers.sort()).toEqual(allocations.sort()); | ||
@@ -381,3 +327,5 @@ expect( | ||
.map((v) => | ||
String.fromCharCode(carrier.uint32[v / Uint32Array.BYTES_PER_ELEMENT]) | ||
String.fromCharCode( | ||
carrier.heap.Uint32Array[v / Uint32Array.BYTES_PER_ELEMENT] | ||
) | ||
) | ||
@@ -384,0 +332,0 @@ .sort() |
@@ -1,19 +0,13 @@ | ||
import { MAP_MACHINE, NODE_MACHINE } from "./memoryLayout"; | ||
import { GlobalCarrier, ExternalArgs } from "../interfaces"; | ||
import { | ||
GlobalCarrier, | ||
ExternalArgs, | ||
NumberEntry, | ||
StringEntry, | ||
} from "../interfaces"; | ||
import { | ||
hashCodeInPlace, | ||
hashCodeExternalValue, | ||
getKeyStartLength, | ||
getKeyStart, | ||
getKeyLength, | ||
} from "./hashmapUtils"; | ||
import { primitiveValueToEntry } from "../utils"; | ||
import { strByteLength } from "../utils"; | ||
import { stringEncodeInto } from "../stringEncodeInto"; | ||
import { | ||
sizeOfEntry, | ||
writeEntry, | ||
readEntry, | ||
compareStringOrNumberEntriesInPlace, | ||
readNumberOrString, | ||
} from "../store"; | ||
@@ -30,3 +24,32 @@ import { ENTRY_TYPE } from "../entry-types"; | ||
import { MemoryOperator } from "../memoryMachinery"; | ||
import { | ||
number_size, | ||
string_size, | ||
number_set_all, | ||
string_set_all, | ||
number_value_place, | ||
number_value_ctor, | ||
typeOnly_type_get, | ||
string_charsPointer_get, | ||
hashmap_set_all, | ||
hashmap_CAPACITY_get, | ||
hashmap_ARRAY_POINTER_get, | ||
hashmap_USED_CAPACITY_set, | ||
hashmap_USED_CAPACITY_get, | ||
hashmapNode_KEY_POINTER_get, | ||
hashmapNode_NEXT_NODE_POINTER_place, | ||
hashmapNode_NEXT_NODE_POINTER_get, | ||
hashmapNode_VALUE_POINTER_place, | ||
hashmap_LINKED_LIST_POINTER_get, | ||
hashmap_LINKED_LIST_SIZE_set, | ||
hashmap_LINKED_LIST_SIZE_get, | ||
hashmapNode_set_all, | ||
hashmap_ARRAY_POINTER_set, | ||
hashmap_CAPACITY_set, | ||
hashmapNode_NEXT_NODE_POINTER_set, | ||
hashmap_size, | ||
hashmapNode_size, | ||
hashmapNode_LINKED_LIST_ITEM_POINTER_get, | ||
} from "../generatedStructs"; | ||
import { Heap } from "../../structsGenerator/consts"; | ||
@@ -40,4 +63,5 @@ export function createHashMap( | ||
) { | ||
const hashMapMemory = carrier.allocator.calloc(MAP_MACHINE.map.SIZE_OF); | ||
const arrayMemory = carrier.allocator.calloc( | ||
const { heap, allocator } = carrier; | ||
const hashMapMemory = allocator.calloc(hashmap_size); | ||
const arrayMemory = allocator.calloc( | ||
initialCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
@@ -48,10 +72,12 @@ ); | ||
const mapMachine = MAP_MACHINE.createOperator(carrier, hashMapMemory); | ||
hashmap_set_all( | ||
heap, | ||
hashMapMemory, | ||
arrayMemory, | ||
linkedListPointer, | ||
0, | ||
initialCapacity, | ||
0 | ||
); | ||
mapMachine.set("ARRAY_POINTER", arrayMemory); | ||
mapMachine.set("CAPACITY", initialCapacity); | ||
mapMachine.set("USED_CAPACITY", 0); | ||
mapMachine.set("LINKED_LIST_POINTER", linkedListPointer); | ||
mapMachine.set("LINKED_LIST_SIZE", 0); | ||
return hashMapMemory; | ||
@@ -69,77 +95,111 @@ } | ||
) { | ||
const mapOperator = MAP_MACHINE.createOperator(carrier, mapPointer); | ||
const keyEntry = primitiveValueToEntry(externalKeyValue) as | ||
| NumberEntry | ||
| StringEntry; | ||
const { heap, allocator } = carrier; | ||
// allocate all possible needed memory upfront, so we won't oom in the middle of something | ||
// in case of overwrite, we will not need this memory | ||
const memoryForNewNode = carrier.allocator.calloc(NODE_MACHINE.map.SIZE_OF); | ||
const memorySizeOfKey = sizeOfEntry(keyEntry); | ||
const keyEntryMemory = carrier.allocator.calloc(memorySizeOfKey); | ||
const memoryForNewNode = allocator.calloc(hashmapNode_size); | ||
let keyMemoryEntryPointer; | ||
let keyDataMemoryStart: number; | ||
let keyDataMemoryLength: number; | ||
writeEntry(carrier, keyEntryMemory, keyEntry); | ||
if (typeof externalKeyValue === "number") { | ||
keyMemoryEntryPointer = allocator.calloc(number_size); | ||
number_set_all( | ||
carrier.heap, | ||
keyMemoryEntryPointer, | ||
ENTRY_TYPE.NUMBER, | ||
externalKeyValue | ||
); | ||
const keyHeaderOverhead = | ||
keyEntry.type === ENTRY_TYPE.STRING | ||
? // type + string length | ||
8 + 4 | ||
: // type | ||
8; | ||
keyDataMemoryStart = keyMemoryEntryPointer + number_value_place; | ||
keyDataMemoryLength = number_value_ctor.BYTES_PER_ELEMENT; | ||
} else { | ||
keyMemoryEntryPointer = allocator.calloc(string_size); | ||
keyDataMemoryLength = strByteLength(externalKeyValue); | ||
keyDataMemoryStart = allocator.calloc(keyDataMemoryLength); | ||
stringEncodeInto( | ||
carrier.heap.Uint8Array, | ||
keyDataMemoryStart, | ||
externalKeyValue | ||
); | ||
string_set_all( | ||
carrier.heap, | ||
keyMemoryEntryPointer, | ||
ENTRY_TYPE.STRING, | ||
keyDataMemoryLength, | ||
keyDataMemoryStart | ||
); | ||
} | ||
const keyHashCode = hashCodeInPlace( | ||
carrier.uint8, | ||
mapOperator.get("CAPACITY"), | ||
// + 1 for the type of key | ||
keyEntryMemory + keyHeaderOverhead, | ||
memorySizeOfKey - keyHeaderOverhead | ||
heap.Uint8Array, | ||
hashmap_CAPACITY_get(heap, mapPointer), | ||
keyDataMemoryStart, | ||
keyDataMemoryLength | ||
); | ||
let ptrToPtrToSaveTheNodeTo = | ||
mapOperator.get("ARRAY_POINTER") + | ||
const bucketStartPointer = | ||
hashmap_ARRAY_POINTER_get(heap, mapPointer) + | ||
keyHashCode * Uint32Array.BYTES_PER_ELEMENT; | ||
const commonNodeOperator = NODE_MACHINE.createOperator( | ||
carrier, | ||
carrier.uint32[ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT] | ||
); | ||
let ptrToPtrToSaveTheNodeTo = bucketStartPointer; | ||
let iteratedNodePointer = | ||
heap.Uint32Array[ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT]; | ||
// todo: share code with hashMapNodeLookup? | ||
while ( | ||
commonNodeOperator.startAddress !== 0 && | ||
iteratedNodePointer !== 0 && | ||
!compareStringOrNumberEntriesInPlace( | ||
carrier, | ||
commonNodeOperator.get("KEY_POINTER"), | ||
keyEntryMemory | ||
carrier.heap, | ||
hashmapNode_KEY_POINTER_get(heap, iteratedNodePointer), | ||
keyMemoryEntryPointer | ||
) | ||
) { | ||
ptrToPtrToSaveTheNodeTo = commonNodeOperator.pointerTo("NEXT_NODE_POINTER"); | ||
commonNodeOperator.startAddress = commonNodeOperator.get( | ||
"NEXT_NODE_POINTER" | ||
ptrToPtrToSaveTheNodeTo = | ||
iteratedNodePointer + hashmapNode_NEXT_NODE_POINTER_place; | ||
iteratedNodePointer = hashmapNode_NEXT_NODE_POINTER_get( | ||
heap, | ||
iteratedNodePointer | ||
); | ||
} | ||
// first item added to bucket | ||
if ( | ||
ptrToPtrToSaveTheNodeTo === | ||
mapOperator.get("ARRAY_POINTER") + | ||
keyHashCode * Uint32Array.BYTES_PER_ELEMENT | ||
) { | ||
mapOperator.set("USED_CAPACITY", mapOperator.get("USED_CAPACITY") + 1); | ||
// bucket was empty, first item added to bucket | ||
if (ptrToPtrToSaveTheNodeTo === bucketStartPointer) { | ||
hashmap_USED_CAPACITY_set( | ||
heap, | ||
mapPointer, | ||
hashmap_USED_CAPACITY_get(heap, mapPointer) + 1 | ||
); | ||
} | ||
// found node with same key, return same pointer | ||
if (commonNodeOperator.startAddress !== 0) { | ||
if (iteratedNodePointer !== 0) { | ||
// we don't need the new memory | ||
carrier.allocator.free(keyEntryMemory); | ||
carrier.allocator.free(memoryForNewNode); | ||
// @todo Free here also the string data | ||
if ( | ||
typeOnly_type_get(carrier.heap, keyMemoryEntryPointer) === | ||
ENTRY_TYPE.STRING | ||
) { | ||
allocator.free( | ||
string_charsPointer_get(carrier.heap, keyMemoryEntryPointer) | ||
); | ||
} | ||
return commonNodeOperator.pointerTo("VALUE_POINTER"); | ||
// we don't need to new memory | ||
allocator.free(keyMemoryEntryPointer); | ||
allocator.free(memoryForNewNode); | ||
return iteratedNodePointer + hashmapNode_VALUE_POINTER_place; | ||
} else { | ||
commonNodeOperator.startAddress = memoryForNewNode; | ||
commonNodeOperator.set("KEY_POINTER", keyEntryMemory); | ||
commonNodeOperator.set( | ||
"LINKED_LIST_ITEM_POINTER", | ||
iteratedNodePointer = memoryForNewNode; | ||
hashmapNode_set_all( | ||
heap, | ||
iteratedNodePointer, | ||
0, | ||
0, | ||
keyMemoryEntryPointer, | ||
linkedListItemInsert( | ||
carrier, | ||
mapOperator.get("LINKED_LIST_POINTER"), | ||
hashmap_LINKED_LIST_POINTER_get(heap, mapPointer), | ||
memoryForNewNode | ||
@@ -149,9 +209,10 @@ ) | ||
carrier.uint32[ | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTheNodeTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = memoryForNewNode; | ||
mapOperator.set( | ||
"LINKED_LIST_SIZE", | ||
mapOperator.get("LINKED_LIST_SIZE") + 1 | ||
hashmap_LINKED_LIST_SIZE_set( | ||
heap, | ||
mapPointer, | ||
hashmap_LINKED_LIST_SIZE_get(heap, mapPointer) + 1 | ||
); | ||
@@ -161,15 +222,15 @@ | ||
shouldRehash( | ||
mapOperator.get("LINKED_LIST_SIZE"), | ||
mapOperator.get("CAPACITY"), | ||
mapOperator.get("USED_CAPACITY"), | ||
hashmap_CAPACITY_get(heap, mapPointer), | ||
hashmap_USED_CAPACITY_get(heap, mapPointer), | ||
externalArgs.hashMapLoadFactor | ||
) | ||
) { | ||
// console.log("rehash", { | ||
// USED_CAPACITY: mapOperator.get("USED_CAPACITY") | ||
// }); | ||
hashMapRehash(carrier, mapOperator, mapOperator.get("CAPACITY") * 2); | ||
hashMapRehash( | ||
carrier, | ||
mapPointer, | ||
hashmap_CAPACITY_get(heap, mapPointer) * 2 | ||
); | ||
} | ||
return commonNodeOperator.pointerTo("VALUE_POINTER"); | ||
return iteratedNodePointer + hashmapNode_VALUE_POINTER_place; | ||
} | ||
@@ -182,33 +243,31 @@ } | ||
export function hashMapNodeLookup( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
mapPointer: number, | ||
externalKeyValue: number | string | ||
) { | ||
const mapMachine = MAP_MACHINE.createOperator(carrier, mapPointer); | ||
const keyHashCode = hashCodeExternalValue( | ||
mapMachine.get("CAPACITY"), | ||
hashmap_CAPACITY_get(heap, mapPointer), | ||
externalKeyValue | ||
); | ||
let ptrToPtr = | ||
mapMachine.get("ARRAY_POINTER") + | ||
const bucketStartPtrToPtr = | ||
hashmap_ARRAY_POINTER_get(heap, mapPointer) + | ||
keyHashCode * Uint32Array.BYTES_PER_ELEMENT; | ||
const node = NODE_MACHINE.createOperator( | ||
carrier, | ||
carrier.uint32[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT] | ||
); | ||
let ptrToPtr = bucketStartPtrToPtr; | ||
let iteratedNode = | ||
heap.Uint32Array[bucketStartPtrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
while (node.startAddress !== 0) { | ||
const keyEntry = readEntry(carrier, node.get("KEY_POINTER")) as | ||
| NumberEntry | ||
| StringEntry; | ||
while (iteratedNode !== 0) { | ||
const keyValue = readNumberOrString( | ||
heap, | ||
hashmapNode_KEY_POINTER_get(heap, iteratedNode) | ||
); | ||
if (keyEntry.value === externalKeyValue) { | ||
if (keyValue === externalKeyValue) { | ||
return ptrToPtr; | ||
} | ||
ptrToPtr = node.pointerTo("NEXT_NODE_POINTER"); | ||
node.startAddress = node.get("NEXT_NODE_POINTER"); | ||
ptrToPtr = iteratedNode + hashmapNode_NEXT_NODE_POINTER_place; | ||
iteratedNode = hashmapNode_NEXT_NODE_POINTER_get(heap, iteratedNode); | ||
} | ||
@@ -220,7 +279,7 @@ | ||
export function hashMapValueLookup( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
mapPointer: number, | ||
externalKeyValue: number | string | ||
) { | ||
const nodePtrToPtr = hashMapNodeLookup(carrier, mapPointer, externalKeyValue); | ||
const nodePtrToPtr = hashMapNodeLookup(heap, mapPointer, externalKeyValue); | ||
@@ -231,8 +290,6 @@ if (nodePtrToPtr === 0) { | ||
const node = NODE_MACHINE.createOperator( | ||
carrier, | ||
carrier.uint32[nodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT] | ||
return ( | ||
heap.Uint32Array[nodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT] + | ||
hashmapNode_VALUE_POINTER_place | ||
); | ||
return node.pointerTo("VALUE_POINTER"); | ||
} | ||
@@ -248,4 +305,5 @@ | ||
) { | ||
const { heap, allocator } = carrier; | ||
const foundNodePtrToPtr = hashMapNodeLookup( | ||
carrier, | ||
heap, | ||
mapPointer, | ||
@@ -260,26 +318,38 @@ externalKeyValue | ||
const nodeToDeletePointer = | ||
carrier.uint32[foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
heap.Uint32Array[foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
const valuePointer = nodeToDeletePointer + hashmapNode_VALUE_POINTER_place; | ||
const nodeOperator = NODE_MACHINE.createOperator( | ||
linkedListItemRemove( | ||
carrier, | ||
nodeToDeletePointer | ||
hashmapNode_LINKED_LIST_ITEM_POINTER_get(heap, nodeToDeletePointer) | ||
); | ||
const valuePointer = nodeOperator.pointerTo("VALUE_POINTER"); | ||
linkedListItemRemove(carrier, nodeOperator.get("LINKED_LIST_ITEM_POINTER")); | ||
// remove node from bucket | ||
carrier.uint32[ | ||
heap.Uint32Array[ | ||
foundNodePtrToPtr / Uint32Array.BYTES_PER_ELEMENT | ||
] = nodeOperator.get("NEXT_NODE_POINTER"); | ||
] = hashmapNode_NEXT_NODE_POINTER_get(heap, nodeToDeletePointer); | ||
carrier.allocator.free(nodeOperator.get("KEY_POINTER")); | ||
carrier.allocator.free(nodeOperator.startAddress); | ||
if ( | ||
typeOnly_type_get( | ||
heap, | ||
hashmapNode_KEY_POINTER_get(heap, nodeToDeletePointer) | ||
) === ENTRY_TYPE.STRING | ||
) { | ||
allocator.free( | ||
string_charsPointer_get( | ||
heap, | ||
hashmapNode_KEY_POINTER_get(heap, nodeToDeletePointer) | ||
) | ||
); | ||
} | ||
carrier.uint32[ | ||
(mapPointer + MAP_MACHINE.map.LINKED_LIST_SIZE.bytesOffset) / | ||
Uint32Array.BYTES_PER_ELEMENT | ||
]--; | ||
allocator.free(hashmapNode_KEY_POINTER_get(heap, nodeToDeletePointer)); | ||
allocator.free(nodeToDeletePointer); | ||
hashmap_LINKED_LIST_SIZE_set( | ||
heap, | ||
mapPointer, | ||
hashmap_LINKED_LIST_SIZE_get(heap, mapPointer) - 1 | ||
); | ||
return valuePointer; | ||
@@ -293,19 +363,18 @@ } | ||
export function hashMapLowLevelIterator( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
mapPointer: number, | ||
nodePointerIteratorToken: number | ||
) { | ||
const mapOperator = MAP_MACHINE.createOperator(carrier, mapPointer); | ||
let tokenToUseForLinkedListIterator = 0; | ||
if (nodePointerIteratorToken !== 0) { | ||
tokenToUseForLinkedListIterator = NODE_MACHINE.createOperator( | ||
carrier, | ||
tokenToUseForLinkedListIterator = hashmapNode_LINKED_LIST_ITEM_POINTER_get( | ||
heap, | ||
nodePointerIteratorToken | ||
).get("LINKED_LIST_ITEM_POINTER"); | ||
); | ||
} | ||
const pointerToNextLinkedListItem = linkedListLowLevelIterator( | ||
carrier, | ||
mapOperator.get("LINKED_LIST_POINTER"), | ||
heap, | ||
hashmap_LINKED_LIST_POINTER_get(heap, mapPointer), | ||
tokenToUseForLinkedListIterator | ||
@@ -318,46 +387,50 @@ ); | ||
return linkedListGetValue(carrier, pointerToNextLinkedListItem); | ||
return linkedListGetValue(heap, pointerToNextLinkedListItem); | ||
} | ||
export function hashMapNodePointerToKeyValue( | ||
carrier: GlobalCarrier, | ||
nodePointer: number | ||
) { | ||
const operator = NODE_MACHINE.createOperator(carrier, nodePointer); | ||
export function hashMapNodePointerToKeyValue(heap: Heap, nodePointer: number) { | ||
// @todo avoid intermediate object | ||
return { | ||
valuePointer: operator.pointerTo("VALUE_POINTER"), | ||
keyPointer: operator.get("KEY_POINTER"), | ||
valuePointer: nodePointer + hashmapNode_VALUE_POINTER_place, | ||
keyPointer: hashmapNode_KEY_POINTER_get(heap, nodePointer), | ||
}; | ||
} | ||
export function hashMapSize(carrier: GlobalCarrier, mapPointer: number) { | ||
return carrier.uint32[ | ||
(mapPointer + MAP_MACHINE.map.LINKED_LIST_SIZE.bytesOffset) / | ||
Uint32Array.BYTES_PER_ELEMENT | ||
]; | ||
export function hashMapSize(heap: Heap, mapPointer: number) { | ||
return hashmap_LINKED_LIST_SIZE_get(heap, mapPointer); | ||
} | ||
export function hashMapGetPointersToFree( | ||
carrier: GlobalCarrier, | ||
hashmapPointer: number | ||
) { | ||
const mapOperator = MAP_MACHINE.createOperator(carrier, hashmapPointer); | ||
const pointers: number[] = [hashmapPointer, mapOperator.get("ARRAY_POINTER")]; | ||
export function hashMapGetPointersToFree(heap: Heap, hashmapPointer: number) { | ||
const pointers: number[] = [ | ||
hashmapPointer, | ||
hashmap_ARRAY_POINTER_get(heap, hashmapPointer), | ||
]; | ||
const pointersToValuePointers: number[] = []; | ||
const pointersOfLinkedList = linkedListGetPointersToFree( | ||
carrier, | ||
mapOperator.get("LINKED_LIST_POINTER") | ||
heap, | ||
hashmap_LINKED_LIST_POINTER_get(heap, hashmapPointer) | ||
); | ||
pointers.push(...pointersOfLinkedList.pointers); | ||
const nodeOperator = NODE_MACHINE.createOperator(carrier, 0); | ||
for (const nodePointer of pointersOfLinkedList.valuePointers) { | ||
nodeOperator.startAddress = nodePointer; | ||
pointersToValuePointers.push(nodeOperator.pointerTo("VALUE_POINTER")); | ||
pointers.push(nodePointer, nodeOperator.get("KEY_POINTER")); | ||
pointersToValuePointers.push(nodePointer + hashmapNode_VALUE_POINTER_place); | ||
if ( | ||
typeOnly_type_get( | ||
heap, | ||
hashmapNode_KEY_POINTER_get(heap, nodePointer) | ||
) === ENTRY_TYPE.STRING | ||
) { | ||
pointers.push( | ||
string_charsPointer_get( | ||
heap, | ||
hashmapNode_KEY_POINTER_get(heap, nodePointer) | ||
) | ||
); | ||
} | ||
pointers.push(nodePointer, hashmapNode_KEY_POINTER_get(heap, nodePointer)); | ||
} | ||
// @todo avoid intermediate object | ||
return { | ||
@@ -371,18 +444,9 @@ pointers, | ||
carrier: GlobalCarrier, | ||
mapOperator: MemoryOperator< | ||
| "CAPACITY" | ||
| "USED_CAPACITY" | ||
| "ARRAY_POINTER" | ||
| "LINKED_LIST_POINTER" | ||
| "LINKED_LIST_SIZE" | ||
>, | ||
hashmapPointer: number, | ||
newCapacity: number | ||
) { | ||
// const before = { | ||
// ARRAY_POINTER: mapOperator.get("ARRAY_POINTER"), | ||
// CAPACITY: mapOperator.get("CAPACITY"), | ||
// USED_CAPACITY: mapOperator.get("USED_CAPACITY") | ||
// }; | ||
const { heap, allocator } = carrier; | ||
carrier.allocator.free(mapOperator.get("ARRAY_POINTER")); | ||
// Why not realloc? | ||
allocator.free(hashmap_ARRAY_POINTER_get(heap, hashmapPointer)); | ||
const biggerArray = carrier.allocator.calloc( | ||
@@ -392,26 +456,14 @@ newCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
// (carrier.allocator as typeof carrier.allocator & { u8: Uint8Array }).u8.fill( | ||
// 0, | ||
// biggerArray, | ||
// biggerArray + newCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
// ); | ||
hashmap_ARRAY_POINTER_set(heap, hashmapPointer, biggerArray); | ||
hashmap_CAPACITY_set(heap, hashmapPointer, newCapacity); | ||
mapOperator.set("ARRAY_POINTER", biggerArray); | ||
mapOperator.set("CAPACITY", newCapacity); | ||
// const after = { | ||
// ARRAY_POINTER: mapOperator.get("ARRAY_POINTER"), | ||
// CAPACITY: mapOperator.get("CAPACITY"), | ||
// USED_CAPACITY: mapOperator.get("USED_CAPACITY") | ||
// }; | ||
let pointerToNode = 0; | ||
while ( | ||
(pointerToNode = hashMapLowLevelIterator( | ||
carrier, | ||
mapOperator.startAddress, | ||
heap, | ||
hashmapPointer, | ||
pointerToNode | ||
)) !== 0 | ||
) { | ||
hashMapRehashInsert(carrier, biggerArray, newCapacity, pointerToNode); | ||
hashMapRehashInsert(heap, biggerArray, newCapacity, pointerToNode); | ||
} | ||
@@ -421,3 +473,3 @@ } | ||
function hashMapRehashInsert( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
bucketsArrayPointer: number, | ||
@@ -427,10 +479,7 @@ arraySize: number, | ||
) { | ||
const nodeOperator = NODE_MACHINE.createOperator(carrier, nodePointer); | ||
const keyInfo = getKeyStartLength(carrier, nodeOperator.get("KEY_POINTER")); | ||
const keyHashCode = hashCodeInPlace( | ||
carrier.uint8, | ||
heap.Uint8Array, | ||
arraySize, | ||
keyInfo.start, | ||
keyInfo.length | ||
getKeyStart(heap, nodePointer), | ||
getKeyLength(heap, nodePointer) | ||
); | ||
@@ -443,26 +492,12 @@ | ||
const prevFirstNodeInBucket = | ||
carrier.uint32[bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
heap.Uint32Array[bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT]; | ||
carrier.uint32[ | ||
heap.Uint32Array[ | ||
bucketStartPointer / Uint32Array.BYTES_PER_ELEMENT | ||
] = nodePointer; | ||
nodeOperator.set("NEXT_NODE_POINTER", prevFirstNodeInBucket); | ||
// // Add is first node in bucket | ||
// if (nodeOperator.startAddress === 0) { | ||
// carrier.dataView.setUint32(bucketStartPointer, nodePointer); | ||
// return; | ||
// } | ||
// // find last node. | ||
// while (nodeOperator.get("NEXT_NODE_POINTER") !== 0) { | ||
// nodeOperator.startAddress = nodeOperator.get("NEXT_NODE_POINTER"); | ||
// } | ||
// nodeOperator.set("NEXT_NODE_POINTER", nodePointer); | ||
hashmapNode_NEXT_NODE_POINTER_set(heap, nodePointer, prevFirstNodeInBucket); | ||
} | ||
function shouldRehash( | ||
nodesCount: number, | ||
buckets: number, | ||
@@ -477,6 +512,3 @@ fullBuckets: number, | ||
export function* hashmapNodesPointerIterator( | ||
carrier: GlobalCarrier, | ||
mapPointer: number | ||
) { | ||
export function* hashmapNodesPointerIterator(heap: Heap, mapPointer: number) { | ||
let iteratorToken = 0; | ||
@@ -486,3 +518,3 @@ | ||
(iteratorToken = hashMapLowLevelIterator( | ||
carrier, | ||
heap, | ||
mapPointer, | ||
@@ -489,0 +521,0 @@ iteratorToken |
import { ENTRY_TYPE } from "../entry-types"; | ||
import { stringEncodeInto } from "../stringEncodeInto"; | ||
import { GlobalCarrier } from "../interfaces"; | ||
import { Heap } from "../../structsGenerator/consts"; | ||
import { | ||
number_value_place, | ||
string_charsPointer_get, | ||
typeOnly_type_get, | ||
number_value_ctor, | ||
string_bytesLength_get, | ||
} from "../generatedStructs"; | ||
@@ -41,36 +48,17 @@ export function hashCodeInPlace( | ||
export function hashCodeEntry( | ||
carrier: GlobalCarrier, | ||
capacity: number, | ||
pointer: number | ||
): number { | ||
const type: ENTRY_TYPE.NUMBER | ENTRY_TYPE.STRING = carrier.uint8[pointer]; | ||
if (type === ENTRY_TYPE.NUMBER) { | ||
return hashCodeInPlace(carrier.uint8, capacity, pointer + 1, 8); | ||
export function getKeyStart(heap: Heap, keyPointer: number) { | ||
if (typeOnly_type_get(heap, keyPointer) === ENTRY_TYPE.NUMBER) { | ||
return keyPointer + number_value_place; | ||
} else { | ||
return hashCodeInPlace( | ||
carrier.uint8, | ||
capacity, | ||
pointer + 1 + Uint16Array.BYTES_PER_ELEMENT, | ||
carrier.uint16[(pointer + 1) / Uint16Array.BYTES_PER_ELEMENT] | ||
); | ||
// string | ||
return string_charsPointer_get(heap, keyPointer); | ||
} | ||
} | ||
export function getKeyStartLength(carrier: GlobalCarrier, keyPointer: number) { | ||
if ( | ||
carrier.uint32[keyPointer / Uint32Array.BYTES_PER_ELEMENT] === | ||
ENTRY_TYPE.NUMBER | ||
) { | ||
return { | ||
start: keyPointer + 1, | ||
length: Float64Array.BYTES_PER_ELEMENT, | ||
}; | ||
export function getKeyLength(heap: Heap, keyPointer: number) { | ||
if (typeOnly_type_get(heap, keyPointer) === ENTRY_TYPE.NUMBER) { | ||
return number_value_ctor.BYTES_PER_ELEMENT; | ||
} else { | ||
return { | ||
start: keyPointer + 1 + 2 + 2, | ||
length: carrier.uint16[(keyPointer + 1) / Uint16Array.BYTES_PER_ELEMENT], | ||
}; | ||
return string_bytesLength_get(heap, keyPointer); | ||
} | ||
} |
@@ -1,92 +0,7 @@ | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { TextDecoder, TextEncoder } from "./textEncoderDecoderTypes"; | ||
import type { Heap } from "../structsGenerator/consts"; | ||
import type { IMemPool } from "@thi.ng/malloc"; | ||
export type primitive = string | number | bigint | boolean | undefined | null; | ||
export type Entry = | ||
| StringEntry | ||
| NumberEntry | ||
| BigIntPositiveEntry | ||
| BigIntNegativeEntry | ||
| ObjectEntry | ||
| ArrayEntry | ||
| DateEntry | ||
| MapEntry | ||
| SetEntry; | ||
export interface StringEntry { | ||
type: ENTRY_TYPE.STRING; | ||
value: string; | ||
allocatedBytes: number; | ||
} | ||
export interface NumberEntry { | ||
type: ENTRY_TYPE.NUMBER; | ||
value: number; | ||
} | ||
export interface BigIntPositiveEntry { | ||
type: ENTRY_TYPE.BIGINT_POSITIVE; | ||
value: bigint; | ||
} | ||
export interface BigIntNegativeEntry { | ||
type: ENTRY_TYPE.BIGINT_NEGATIVE; | ||
value: bigint; | ||
} | ||
export interface ObjectEntry { | ||
type: ENTRY_TYPE.OBJECT; | ||
refsCount: number; | ||
/** | ||
* Pointer to hashmap | ||
*/ | ||
value: number; | ||
} | ||
export interface ArrayEntry { | ||
type: ENTRY_TYPE.ARRAY; | ||
refsCount: number; | ||
// pointer to the first array item | ||
value: number; | ||
length: number; | ||
allocatedLength: number; | ||
} | ||
export interface DateEntry { | ||
type: ENTRY_TYPE.DATE; | ||
refsCount: number; | ||
value: number; | ||
} | ||
export interface MapEntry { | ||
type: ENTRY_TYPE.MAP; | ||
refsCount: number; | ||
/** | ||
* Pointer to hashmap | ||
*/ | ||
value: number; | ||
} | ||
export interface SetEntry { | ||
type: ENTRY_TYPE.SET; | ||
refsCount: number; | ||
/** | ||
* Pointer to hashmap | ||
*/ | ||
value: number; | ||
} | ||
/** | ||
* The carrier object allows us to swap the DataView easily | ||
*/ | ||
export interface GlobalCarrier { | ||
// dataView: DataView; | ||
uint8: Uint8Array; | ||
uint16: Uint16Array; | ||
uint32: Uint32Array; | ||
float64: Float64Array; | ||
bigUint64: BigUint64Array; | ||
allocator: import("@thi.ng/malloc").IMemPool; | ||
allocator: IMemPool; | ||
heap: Heap; | ||
} | ||
@@ -97,5 +12,7 @@ | ||
hashMapMinInitialCapacity: number; | ||
/** | ||
* Allocate additional memory for array pointers, | ||
* will prevent the reallocation and copy when array is getting bigger | ||
*/ | ||
arrayAdditionalAllocation: number; | ||
textDecoder: TextDecoder; | ||
textEncoder: TextEncoder; | ||
}>; | ||
@@ -107,4 +24,2 @@ | ||
arrayAdditionalAllocation?: number; | ||
textDecoder: TextDecoder; | ||
textEncoder: TextEncoder; | ||
}>; | ||
@@ -111,0 +26,0 @@ |
@@ -9,6 +9,2 @@ /* eslint-env jest */ | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
// import jestDiff from "jest-diff"; | ||
import { | ||
@@ -111,3 +107,3 @@ linkedListItemInsert, | ||
(iteratorPointer = linkedListLowLevelIterator( | ||
carrier, | ||
carrier.heap, | ||
linkedListPointer, | ||
@@ -117,3 +113,3 @@ iteratorPointer | ||
) { | ||
values.push(linkedListGetValue(carrier, iteratorPointer)); | ||
values.push(linkedListGetValue(carrier.heap, iteratorPointer)); | ||
} | ||
@@ -155,3 +151,3 @@ | ||
(iteratorPointer = linkedListLowLevelIterator( | ||
carrier, | ||
carrier.heap, | ||
linkedListPointer, | ||
@@ -161,3 +157,3 @@ iteratorPointer | ||
) { | ||
values.push(linkedListGetValue(carrier, iteratorPointer)); | ||
values.push(linkedListGetValue(carrier.heap, iteratorPointer)); | ||
linkedListItemRemove(carrier, iteratorPointer); | ||
@@ -207,3 +203,3 @@ } | ||
(iteratorPointer = linkedListLowLevelIterator( | ||
carrier, | ||
carrier.heap, | ||
linkedListPointer, | ||
@@ -213,3 +209,3 @@ iteratorPointer | ||
) { | ||
linkedLintResults.push(linkedListGetValue(carrier, iteratorPointer)); | ||
linkedLintResults.push(linkedListGetValue(carrier.heap, iteratorPointer)); | ||
linkedListItemRemove(carrier, iteratorPointer); | ||
@@ -266,3 +262,3 @@ } | ||
(iteratorPointer = linkedListLowLevelIterator( | ||
carrier, | ||
carrier.heap, | ||
linkedListPointer, | ||
@@ -272,3 +268,3 @@ iteratorPointer | ||
) { | ||
values.push(linkedListGetValue(carrier, iteratorPointer)); | ||
values.push(linkedListGetValue(carrier.heap, iteratorPointer)); | ||
if (c < toAdd.length) { | ||
@@ -307,3 +303,3 @@ linkedListItemInsert(carrier, linkedListPointer, toAdd[c++]); | ||
(iteratorPointer = linkedListLowLevelIterator( | ||
carrier, | ||
carrier.heap, | ||
linkedListPointer, | ||
@@ -337,3 +333,3 @@ iteratorPointerToFree | ||
const r = linkedListGetPointersToFree(carrier, linkedListPointer); | ||
const r = linkedListGetPointersToFree(carrier.heap, linkedListPointer); | ||
@@ -340,0 +336,0 @@ expect(r).toMatchInlineSnapshot(` |
@@ -1,3 +0,14 @@ | ||
import { createMemoryMachine } from "../memoryMachinery"; | ||
import { GlobalCarrier } from "../interfaces"; | ||
import type { GlobalCarrier } from "../interfaces"; | ||
import { | ||
linkedListItem_size, | ||
linkedList_size, | ||
linkedList_set_all, | ||
linkedListItem_set_all, | ||
linkedList_END_POINTER_get, | ||
linkedList_END_POINTER_set, | ||
linkedListItem_NEXT_POINTER_get, | ||
linkedListItem_VALUE_get, | ||
linkedList_START_POINTER_get, | ||
} from "../generatedStructs"; | ||
import { Heap } from "../../structsGenerator/consts"; | ||
@@ -19,34 +30,14 @@ /* | ||
export const LINKED_LIST_ITEM_MACHINE = createMemoryMachine({ | ||
NEXT_POINTER: Uint32Array, | ||
VALUE: Uint32Array, | ||
}); | ||
export type LinkedListItemMachineType = ReturnType< | ||
typeof LINKED_LIST_ITEM_MACHINE.createOperator | ||
>; | ||
export const LINKED_LIST_MACHINE = createMemoryMachine({ | ||
END_POINTER: Uint32Array, | ||
START_POINTER: Uint32Array, | ||
}); | ||
export type LinkedListMachineType = ReturnType< | ||
typeof LINKED_LIST_MACHINE.createOperator | ||
>; | ||
export function initLinkedList(carrier: GlobalCarrier) { | ||
const { allocator } = carrier; | ||
const memoryForLinkedList = allocator.calloc(LINKED_LIST_MACHINE.map.SIZE_OF); | ||
const memoryForEndMarkerItem = allocator.calloc( | ||
LINKED_LIST_ITEM_MACHINE.map.SIZE_OF | ||
); | ||
const { allocator, heap } = carrier; | ||
const memoryForLinkedList = allocator.calloc(linkedList_size); | ||
const memoryForEndMarkerItem = allocator.calloc(linkedListItem_size); | ||
const linkedListMachine = LINKED_LIST_MACHINE.createOperator( | ||
carrier, | ||
memoryForLinkedList | ||
linkedList_set_all( | ||
heap, | ||
memoryForLinkedList, | ||
memoryForEndMarkerItem, | ||
memoryForEndMarkerItem | ||
); | ||
linkedListMachine.set("START_POINTER", memoryForEndMarkerItem); | ||
linkedListMachine.set("END_POINTER", memoryForEndMarkerItem); | ||
return memoryForLinkedList; | ||
@@ -60,73 +51,43 @@ } | ||
) { | ||
const newItemMemory: number = carrier.allocator.calloc( | ||
LINKED_LIST_ITEM_MACHINE.map.SIZE_OF | ||
); | ||
const { allocator, heap } = carrier; | ||
const newEndMarker = allocator.calloc(linkedListItem_size); | ||
const linkedListOperator = LINKED_LIST_MACHINE.createOperator( | ||
carrier, | ||
linkedListPointer | ||
); | ||
linkedListItem_set_all(heap, newEndMarker, 0, 0); | ||
const wasEndMarker = linkedList_END_POINTER_get(heap, linkedListPointer); | ||
linkedList_END_POINTER_set(heap, linkedListPointer, newEndMarker); | ||
const wasEndMarkerOperator = LINKED_LIST_ITEM_MACHINE.createOperator( | ||
carrier, | ||
linkedListOperator.get("END_POINTER") | ||
); | ||
linkedListItem_set_all(heap, wasEndMarker, newEndMarker, nodeValuePointer); | ||
const toBeEndMarkerOperator = LINKED_LIST_ITEM_MACHINE.createOperator( | ||
carrier, | ||
newItemMemory | ||
); | ||
toBeEndMarkerOperator.set("VALUE", 0); | ||
toBeEndMarkerOperator.set("NEXT_POINTER", 0); | ||
linkedListOperator.set("END_POINTER", toBeEndMarkerOperator.startAddress); | ||
wasEndMarkerOperator.set("VALUE", toBeEndMarkerOperator.startAddress); | ||
wasEndMarkerOperator.set("NEXT_POINTER", toBeEndMarkerOperator.startAddress); | ||
wasEndMarkerOperator.set("VALUE", nodeValuePointer); | ||
return wasEndMarkerOperator.startAddress; | ||
return wasEndMarker; | ||
} | ||
export function linkedListItemRemove( | ||
carrier: GlobalCarrier, | ||
{ heap, allocator }: GlobalCarrier, | ||
itemPointer: number | ||
) { | ||
const itemToOverwrite = LINKED_LIST_ITEM_MACHINE.createOperator( | ||
carrier, | ||
itemPointer | ||
const memoryToFree = linkedListItem_NEXT_POINTER_get(heap, itemPointer); | ||
linkedListItem_set_all( | ||
heap, | ||
itemPointer, | ||
linkedListItem_NEXT_POINTER_get(heap, memoryToFree), | ||
linkedListItem_VALUE_get(heap, memoryToFree) | ||
); | ||
const itemToOverwriteWith = LINKED_LIST_ITEM_MACHINE.createOperator( | ||
carrier, | ||
itemToOverwrite.get("NEXT_POINTER") | ||
); | ||
const memoryToFree = itemToOverwrite.get("NEXT_POINTER"); | ||
itemToOverwrite.set("VALUE", itemToOverwriteWith.get("VALUE")); | ||
itemToOverwrite.set("NEXT_POINTER", itemToOverwriteWith.get("NEXT_POINTER")); | ||
carrier.allocator.free(memoryToFree); | ||
allocator.free(memoryToFree); | ||
} | ||
export function linkedListLowLevelIterator( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
linkedListPointer: number, | ||
itemPointer: number | ||
) { | ||
const listItem = LINKED_LIST_ITEM_MACHINE.createOperator( | ||
carrier, | ||
itemPointer | ||
); | ||
let iteratedItem = itemPointer; | ||
// new iteration session | ||
if (itemPointer === 0) { | ||
const list = LINKED_LIST_MACHINE.createOperator(carrier, linkedListPointer); | ||
// read the first item in the list | ||
iteratedItem = linkedList_START_POINTER_get(heap, linkedListPointer); | ||
listItem.startAddress = list.get("START_POINTER"); | ||
// can be zero if START_POINTER pointes to the end marker | ||
if (listItem.get("VALUE") === 0) { | ||
if (linkedListItem_VALUE_get(heap, iteratedItem) === 0) { | ||
return 0; | ||
@@ -136,31 +97,26 @@ } | ||
// can be zero if START_POINTER pointes to the end marker | ||
return listItem.startAddress; | ||
return iteratedItem; | ||
} | ||
// deleted during iteration | ||
if (listItem.get("VALUE") === 0) { | ||
if (linkedListItem_VALUE_get(heap, iteratedItem) === 0) { | ||
return 0; | ||
} | ||
listItem.startAddress = listItem.get("NEXT_POINTER"); | ||
iteratedItem = linkedListItem_NEXT_POINTER_get(heap, iteratedItem); | ||
// next item is the end | ||
if (listItem.get("VALUE") === 0) { | ||
if (linkedListItem_VALUE_get(heap, iteratedItem) === 0) { | ||
return 0; | ||
} | ||
return listItem.startAddress; | ||
return iteratedItem; | ||
} | ||
export function linkedListGetValue( | ||
carrier: GlobalCarrier, | ||
itemPointer: number | ||
) { | ||
return LINKED_LIST_ITEM_MACHINE.createOperator(carrier, itemPointer).get( | ||
"VALUE" | ||
); | ||
export function linkedListGetValue(heap: Heap, itemPointer: number) { | ||
return linkedListItem_VALUE_get(heap, itemPointer); | ||
} | ||
export function linkedListGetPointersToFree( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
linkedListPointer: number | ||
@@ -171,10 +127,5 @@ ) { | ||
const operator = LINKED_LIST_MACHINE.createOperator( | ||
carrier, | ||
linkedListPointer | ||
); | ||
const firstItem = linkedList_START_POINTER_get(heap, linkedListPointer); | ||
const lastItem = linkedList_END_POINTER_get(heap, linkedListPointer); | ||
const firstItem = operator.get("START_POINTER"); | ||
const lastItem = operator.get("END_POINTER"); | ||
// list empty | ||
@@ -185,19 +136,16 @@ if (firstItem === lastItem) { | ||
const linkItemOperator = LINKED_LIST_ITEM_MACHINE.createOperator( | ||
carrier, | ||
linkedListLowLevelIterator(carrier, linkedListPointer, 0) | ||
); | ||
let iterator = linkedListLowLevelIterator(heap, linkedListPointer, 0); | ||
while (linkItemOperator.startAddress !== 0) { | ||
pointers.push(linkItemOperator.startAddress); | ||
while (iterator !== 0) { | ||
pointers.push(iterator); | ||
// value = 0 means end marker | ||
if (linkItemOperator.get("VALUE") !== 0) { | ||
valuePointers.push(linkItemOperator.get("VALUE")); | ||
// linkItemOperator.startAddress = 0; | ||
if (linkedListItem_VALUE_get(heap, iterator) !== 0) { | ||
valuePointers.push(linkedListItem_VALUE_get(heap, iterator)); | ||
} | ||
linkItemOperator.startAddress = linkItemOperator.get("NEXT_POINTER"); | ||
iterator = linkedListItem_NEXT_POINTER_get(heap, iterator); | ||
} | ||
// @todo avoid intermediate object | ||
return { | ||
@@ -204,0 +152,0 @@ pointers, |
@@ -0,1 +1,5 @@ | ||
/* istanbul ignore file */ | ||
// I'm not sure how to test that yet | ||
import { invariant } from "./utils"; | ||
@@ -2,0 +6,0 @@ import { getUnderlyingArrayBuffer } from "./api"; |
@@ -0,8 +1,3 @@ | ||
import { ExternalArgs, GlobalCarrier, InternalAPI } from "./interfaces"; | ||
import { | ||
ExternalArgs, | ||
GlobalCarrier, | ||
MapEntry, | ||
InternalAPI, | ||
} from "./interfaces"; | ||
import { | ||
deleteObjectPropertyEntryByKey, | ||
@@ -25,5 +20,5 @@ objectGet, | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { object_pointerToHashMap_get } from "./generatedStructs"; | ||
export class MapWrapper<K extends string | number, V> | ||
extends BaseProxyTrap<MapEntry> | ||
export class MapWrapper<K extends string | number, V> extends BaseProxyTrap | ||
implements Map<K, V> { | ||
@@ -44,3 +39,6 @@ clear(): void { | ||
get size(): number { | ||
return hashMapSize(this.carrier, this.entry.value); | ||
return hashMapSize( | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
); | ||
} | ||
@@ -54,7 +52,7 @@ | ||
for (const nodePointer of hashmapNodesPointerIterator( | ||
this.carrier, | ||
this.entry.value | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
)) { | ||
const { valuePointer, keyPointer } = hashMapNodePointerToKeyValue( | ||
this.carrier, | ||
this.carrier.heap, | ||
nodePointer | ||
@@ -72,3 +70,5 @@ ); | ||
this.carrier, | ||
this.carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT] | ||
this.carrier.heap.Uint32Array[ | ||
valuePointer / Uint32Array.BYTES_PER_ELEMENT | ||
] | ||
), | ||
@@ -81,6 +81,6 @@ ]; | ||
for (const nodePointer of hashmapNodesPointerIterator( | ||
this.carrier, | ||
this.entry.value | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
@@ -97,7 +97,7 @@ yield entryToFinalJavaScriptValue( | ||
for (const nodePointer of hashmapNodesPointerIterator( | ||
this.carrier, | ||
this.entry.value | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
)) { | ||
const { valuePointer } = hashMapNodePointerToKeyValue( | ||
this.carrier, | ||
this.carrier.heap, | ||
nodePointer | ||
@@ -109,3 +109,5 @@ ); | ||
this.carrier, | ||
this.carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT] | ||
this.carrier.heap.Uint32Array[ | ||
valuePointer / Uint32Array.BYTES_PER_ELEMENT | ||
] | ||
); | ||
@@ -132,3 +134,8 @@ } | ||
return objectGet(this.externalArgs, this.carrier, this.entry.value, p); | ||
return objectGet( | ||
this.externalArgs, | ||
this.carrier, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
); | ||
} | ||
@@ -142,5 +149,4 @@ | ||
return deleteObjectPropertyEntryByKey( | ||
this.externalArgs, | ||
this.carrier, | ||
this.entry.value, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
@@ -155,3 +161,9 @@ ); | ||
return hashMapNodeLookup(this.carrier, this.entry.value, p) !== 0; | ||
return ( | ||
hashMapNodeLookup( | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
) !== 0 | ||
); | ||
} | ||
@@ -165,3 +177,9 @@ | ||
allocationsTransaction(() => { | ||
objectSet(this.externalArgs, this.carrier, this.entry.value, p, value); | ||
objectSet( | ||
this.externalArgs, | ||
this.carrier, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p, | ||
value | ||
); | ||
}, this.carrier.allocator); | ||
@@ -168,0 +186,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { ObjectEntry, ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { | ||
@@ -17,6 +17,7 @@ getObjectPropertiesEntries, | ||
import { hashMapNodeLookup } from "./hashmap/hashmap"; | ||
import { object_pointerToHashMap_get } from "./generatedStructs"; | ||
export class ObjectWrapper extends BaseProxyTrap<ObjectEntry> | ||
implements ProxyHandler<{}> { | ||
public get(target: {}, p: PropertyKey): any { | ||
export class ObjectWrapper extends BaseProxyTrap | ||
implements ProxyHandler<Record<string, unknown>> { | ||
public get(target: Record<string, unknown>, p: PropertyKey): any { | ||
if (p === INTERNAL_API_SYMBOL) { | ||
@@ -30,6 +31,14 @@ return this; | ||
return objectGet(this.externalArgs, this.carrier, this.entry.value, p); | ||
return objectGet( | ||
this.externalArgs, | ||
this.carrier, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
); | ||
} | ||
public deleteProperty(target: {}, p: PropertyKey): boolean { | ||
public deleteProperty( | ||
target: Record<string, unknown>, | ||
p: PropertyKey | ||
): boolean { | ||
if (typeof p === "symbol") { | ||
@@ -40,5 +49,4 @@ return false; | ||
return deleteObjectPropertyEntryByKey( | ||
this.externalArgs, | ||
this.carrier, | ||
this.entry.value, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
@@ -51,3 +59,3 @@ ); | ||
this.carrier, | ||
this.entry.value | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
); | ||
@@ -61,3 +69,3 @@ | ||
this.carrier, | ||
this.entry.value | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
); | ||
@@ -68,3 +76,6 @@ | ||
public getOwnPropertyDescriptor(target: {}, p: PropertyKey) { | ||
public getOwnPropertyDescriptor( | ||
target: Record<string, unknown>, | ||
p: PropertyKey | ||
) { | ||
if (this.has(target, p)) { | ||
@@ -77,3 +88,3 @@ return { configurable: true, enumerable: true }; | ||
public has(target: {}, p: PropertyKey) { | ||
public has(target: Record<string, unknown>, p: PropertyKey) { | ||
if (p === INTERNAL_API_SYMBOL) { | ||
@@ -87,6 +98,16 @@ return true; | ||
return hashMapNodeLookup(this.carrier, this.entry.value, p) !== 0; | ||
return ( | ||
hashMapNodeLookup( | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
) !== 0 | ||
); | ||
} | ||
public set(target: {}, p: PropertyKey, value: any): boolean { | ||
public set( | ||
target: Record<string, unknown>, | ||
p: PropertyKey, | ||
value: any | ||
): boolean { | ||
if (typeof p === "symbol") { | ||
@@ -97,3 +118,9 @@ throw new IllegalObjectPropConfigError(); | ||
allocationsTransaction(() => { | ||
objectSet(this.externalArgs, this.carrier, this.entry.value, p, value); | ||
objectSet( | ||
this.externalArgs, | ||
this.carrier, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p, | ||
value | ||
); | ||
}, this.carrier.allocator); | ||
@@ -116,3 +143,3 @@ | ||
public defineProperty(): // target: {}, | ||
public defineProperty(): // target: Record<string, unknown>, | ||
// p: PropertyKey, | ||
@@ -119,0 +146,0 @@ // attributes: PropertyDescriptor |
@@ -0,16 +1,6 @@ | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { | ||
ExternalArgs, | ||
GlobalCarrier, | ||
StringEntry, | ||
NumberEntry, | ||
MapEntry, | ||
SetEntry, | ||
} from "./interfaces"; | ||
import { | ||
readEntry, | ||
writeValueInPtrToPtrAndHandleMemory, | ||
handleArcForDeletedValuePointer, | ||
decrementRefCount, | ||
writeEntry, | ||
setRefCount, | ||
} from "./store"; | ||
@@ -27,5 +17,13 @@ import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { getAllLinkedAddresses } from "./getAllLinkedAddresses"; | ||
import { | ||
typeOnly_type_get, | ||
number_value_get, | ||
typeAndRc_refsCount_get, | ||
typeAndRc_refsCount_set, | ||
object_pointerToHashMap_set, | ||
} from "./generatedStructs"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { readString } from "./readString"; | ||
export function deleteObjectPropertyEntryByKey( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
@@ -47,7 +45,7 @@ hashmapPointer: number, | ||
const deletedValuePointer = | ||
carrier.uint32[ | ||
carrier.heap.Uint32Array[ | ||
deletedValuePointerToPointer / Uint32Array.BYTES_PER_ELEMENT | ||
]; | ||
handleArcForDeletedValuePointer(externalArgs, carrier, deletedValuePointer); | ||
handleArcForDeletedValuePointer(carrier, deletedValuePointer); | ||
@@ -65,17 +63,22 @@ return true; | ||
while ( | ||
(iterator = hashMapLowLevelIterator(carrier, hashmapPointer, iterator)) | ||
(iterator = hashMapLowLevelIterator(carrier.heap, hashmapPointer, iterator)) | ||
) { | ||
const { valuePointer, keyPointer } = hashMapNodePointerToKeyValue( | ||
carrier, | ||
carrier.heap, | ||
iterator | ||
); | ||
const keyEntry = readEntry(carrier, keyPointer) as | ||
| StringEntry | ||
| NumberEntry; | ||
const typeOfKeyEntry: | ||
| ENTRY_TYPE.NUMBER | ||
| ENTRY_TYPE.STRING = typeOnly_type_get(carrier.heap, keyPointer); | ||
const key = | ||
typeOfKeyEntry === ENTRY_TYPE.NUMBER | ||
? number_value_get(carrier.heap, keyPointer) | ||
: readString(carrier.heap, keyPointer); | ||
foundValues.push({ | ||
valuePointer: | ||
carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT], | ||
key: keyEntry.value, | ||
carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT], | ||
key, | ||
}); | ||
@@ -92,3 +95,3 @@ } | ||
p: string | number, | ||
value: any | ||
value: unknown | ||
) { | ||
@@ -111,12 +114,8 @@ const ptrToPtr = hashMapInsertUpdate( | ||
) { | ||
const valuePointer = hashMapValueLookup(carrier, entryPointer, key); | ||
const valuePointer = hashMapValueLookup(carrier.heap, entryPointer, key); | ||
if (valuePointer === 0) { | ||
return undefined; | ||
} | ||
return entryToFinalJavaScriptValue( | ||
externalArgs, | ||
carrier, | ||
carrier.uint32[valuePointer / Uint32Array.BYTES_PER_ELEMENT] | ||
carrier.heap.Uint32Array[valuePointer / Uint32Array.BYTES_PER_ELEMENT] | ||
); | ||
@@ -155,9 +154,8 @@ } | ||
) { | ||
const entry = readEntry(carrier, mapOrSetPtr) as MapEntry | SetEntry; | ||
// we fake the entry refCount as zero so getAllLinkedAddresses will visit what's needed | ||
const prevCount = setRefCount(carrier, mapOrSetPtr, 0); | ||
const prevCount = typeAndRc_refsCount_get(carrier.heap, mapOrSetPtr); | ||
typeAndRc_refsCount_set(carrier.heap, mapOrSetPtr, 0); | ||
const { leafAddresses, arcAddresses } = getAllLinkedAddresses( | ||
carrier, | ||
carrier.heap, | ||
false, | ||
@@ -182,13 +180,12 @@ mapOrSetPtr | ||
decrementRefCount(externalArgs, carrier, address); | ||
decrementRefCount(carrier.heap, address); | ||
} | ||
// hashmapClearFree(externalArgs, carrier, entry.value); | ||
// Restore real ref count | ||
setRefCount(carrier, mapOrSetPtr, prevCount); | ||
entry.value = createHashMap(carrier, externalArgs.hashMapMinInitialCapacity); | ||
writeEntry(carrier, mapOrSetPtr, entry); | ||
typeAndRc_refsCount_set(carrier.heap, mapOrSetPtr, prevCount); | ||
object_pointerToHashMap_set( | ||
carrier.heap, | ||
mapOrSetPtr, | ||
createHashMap(carrier, externalArgs.hashMapMinInitialCapacity) | ||
); | ||
} |
@@ -0,1 +1,2 @@ | ||
/* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
// @ts-nocheck | ||
@@ -2,0 +3,0 @@ |
@@ -1,12 +0,15 @@ | ||
import { | ||
primitiveValueToEntry, | ||
isPrimitive, | ||
getOurPointerIfApplicable, | ||
} from "./utils"; | ||
import { appendEntry } from "./store"; | ||
import { objectSaver, mapSaver, setSaver } from "./objectSaver"; | ||
import { arraySaver } from "./arraySaver"; | ||
import { getOurPointerIfApplicable, strByteLength } from "./utils"; | ||
import { ExternalArgs, GlobalCarrier } from "./interfaces"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { | ||
number_size, | ||
number_set_all, | ||
bigint_size, | ||
bigint_set_all, | ||
string_size, | ||
string_set_all, | ||
date_size, | ||
date_set_all, | ||
} from "./generatedStructs"; | ||
import { | ||
UNDEFINED_KNOWN_ADDRESS, | ||
@@ -16,64 +19,213 @@ NULL_KNOWN_ADDRESS, | ||
FALSE_KNOWN_ADDRESS, | ||
MAX_64_BIG_INT, | ||
} from "./consts"; | ||
import { arraySaverIterative } from "./arraySaverIterative"; | ||
import { | ||
objectSaverIterative, | ||
mapSaverIterative, | ||
setSaverIterative, | ||
} from "./objectSaverIterative"; | ||
import { stringEncodeInto } from "./stringEncodeInto"; | ||
/** | ||
* Returns pointer for the value | ||
*/ | ||
export function saveValue( | ||
export function saveValueIterative( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
referencedPointers: number[], | ||
value: any | ||
referencedExistingPointers: number[], | ||
initialValuePtrToPtr: number, | ||
initialValue: unknown | ||
) { | ||
let valuePointer = 0; | ||
let maybeOurPointer: number | undefined; | ||
const valuesToSave = [initialValue]; | ||
const pointersToSaveTo = [initialValuePtrToPtr]; | ||
const { | ||
heap: { Uint32Array: uint32 }, | ||
allocator, | ||
heap, | ||
} = carrier; | ||
if (value === undefined) { | ||
return UNDEFINED_KNOWN_ADDRESS; | ||
} | ||
while (valuesToSave.length !== 0) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const valueToSave = valuesToSave.pop()!; | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const ptrToPtrToSaveTo = pointersToSaveTo.pop()!; | ||
if (value === null) { | ||
return NULL_KNOWN_ADDRESS; | ||
} | ||
// Handler well-known values | ||
if (valueToSave === undefined) { | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
if (value === true) { | ||
return TRUE_KNOWN_ADDRESS; | ||
} | ||
if (valueToSave === null) { | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = NULL_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
if (value === false) { | ||
return FALSE_KNOWN_ADDRESS; | ||
} | ||
if (valueToSave === true) { | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = TRUE_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
if (isPrimitive(value)) { | ||
const entry = primitiveValueToEntry(value); | ||
valuePointer = appendEntry(externalArgs, carrier, entry); | ||
} else if ( | ||
(maybeOurPointer = getOurPointerIfApplicable(value, carrier.allocator)) | ||
) { | ||
valuePointer = maybeOurPointer; | ||
referencedPointers.push(valuePointer); | ||
} else if (Array.isArray(value)) { | ||
valuePointer = arraySaver(externalArgs, carrier, referencedPointers, value); | ||
} else if (value instanceof Date) { | ||
valuePointer = appendEntry(externalArgs, carrier, { | ||
type: ENTRY_TYPE.DATE, | ||
refsCount: 1, | ||
value: value.getTime(), | ||
}); | ||
} else if (value instanceof Map) { | ||
valuePointer = mapSaver(externalArgs, carrier, referencedPointers, value); | ||
} else if (value instanceof Set) { | ||
valuePointer = setSaver(externalArgs, carrier, value); | ||
} else if (typeof value === "object") { | ||
valuePointer = objectSaver( | ||
if (valueToSave === false) { | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = FALSE_KNOWN_ADDRESS; | ||
continue; | ||
} | ||
switch (typeof valueToSave) { | ||
case "number": | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = allocator.calloc(number_size); | ||
number_set_all( | ||
heap, | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], | ||
ENTRY_TYPE.NUMBER, | ||
valueToSave | ||
); | ||
continue; | ||
case "string": | ||
// eslint-disable-next-line no-case-declarations | ||
const stringBytesLength = strByteLength(valueToSave); | ||
// eslint-disable-next-line no-case-declarations | ||
const stringDataPointer = allocator.calloc(stringBytesLength); | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = allocator.calloc(string_size); | ||
stringEncodeInto(heap.Uint8Array, stringDataPointer, valueToSave); | ||
string_set_all( | ||
heap, | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], | ||
ENTRY_TYPE.STRING, | ||
stringBytesLength, | ||
stringDataPointer | ||
); | ||
continue; | ||
case "bigint": | ||
if (valueToSave > MAX_64_BIG_INT || valueToSave < -MAX_64_BIG_INT) { | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
// Maybe don't make undefined but throw, or clamp | ||
// throw new Error("MAX_64_BIG_INT"); | ||
} | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = allocator.calloc(bigint_size); | ||
bigint_set_all( | ||
heap, | ||
uint32[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], | ||
valueToSave > 0 | ||
? ENTRY_TYPE.BIGINT_POSITIVE | ||
: ENTRY_TYPE.BIGINT_NEGATIVE, | ||
valueToSave * (valueToSave > 0 ? BigInt("1") : BigInt("-1")) | ||
); | ||
continue; | ||
case "function": | ||
// Nope Nope Nope | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
case "symbol": | ||
// not supported, write undefined | ||
uint32[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = UNDEFINED_KNOWN_ADDRESS; | ||
continue; | ||
// // we will never get here | ||
// case "undefined": | ||
// continue; | ||
// // we will never get here | ||
// case "boolean": | ||
// continue; | ||
} | ||
const maybeOurPointerFromSymbol = getOurPointerIfApplicable( | ||
valueToSave, | ||
carrier.allocator | ||
); | ||
if (maybeOurPointerFromSymbol) { | ||
referencedExistingPointers.push(maybeOurPointerFromSymbol); | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = maybeOurPointerFromSymbol; | ||
continue; | ||
} | ||
if (Array.isArray(valueToSave)) { | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = arraySaverIterative( | ||
externalArgs.arrayAdditionalAllocation, | ||
carrier, | ||
valuesToSave, | ||
pointersToSaveTo, | ||
valueToSave | ||
); | ||
continue; | ||
} | ||
if (valueToSave instanceof Date) { | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = allocator.calloc(date_size); | ||
date_set_all( | ||
heap, | ||
heap.Uint32Array[ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT], | ||
ENTRY_TYPE.DATE, | ||
1, | ||
0, | ||
valueToSave.getTime() | ||
); | ||
continue; | ||
} | ||
if (valueToSave instanceof Map) { | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = mapSaverIterative( | ||
externalArgs, | ||
carrier, | ||
valuesToSave, | ||
pointersToSaveTo, | ||
valueToSave | ||
); | ||
continue; | ||
} | ||
if (valueToSave instanceof Set) { | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = setSaverIterative(externalArgs, carrier, valueToSave); | ||
continue; | ||
} | ||
// Plain object? I hope so | ||
heap.Uint32Array[ | ||
ptrToPtrToSaveTo / Uint32Array.BYTES_PER_ELEMENT | ||
] = objectSaverIterative( | ||
externalArgs, | ||
carrier, | ||
referencedPointers, | ||
value | ||
valuesToSave, | ||
pointersToSaveTo, | ||
valueToSave | ||
); | ||
} else { | ||
throw new Error("unsupported yet"); | ||
} | ||
return valuePointer; | ||
} |
@@ -0,8 +1,3 @@ | ||
import { ExternalArgs, GlobalCarrier, InternalAPI } from "./interfaces"; | ||
import { | ||
ExternalArgs, | ||
GlobalCarrier, | ||
MapEntry, | ||
InternalAPI, | ||
} from "./interfaces"; | ||
import { | ||
deleteObjectPropertyEntryByKey, | ||
@@ -24,5 +19,5 @@ objectSet, | ||
import { entryToFinalJavaScriptValue } from "./entryToFinalJavaScriptValue"; | ||
import { object_pointerToHashMap_get } from "./generatedStructs"; | ||
export class SetWrapper<K extends string | number> | ||
extends BaseProxyTrap<MapEntry> | ||
export class SetWrapper<K extends string | number> extends BaseProxyTrap | ||
implements Set<K> { | ||
@@ -43,3 +38,6 @@ clear(): void { | ||
get size(): number { | ||
return hashMapSize(this.carrier, this.entry.value); | ||
return hashMapSize( | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
); | ||
} | ||
@@ -53,6 +51,6 @@ | ||
for (const nodePointer of hashmapNodesPointerIterator( | ||
this.carrier, | ||
this.entry.value | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
@@ -71,6 +69,6 @@ const key = entryToFinalJavaScriptValue( | ||
for (const nodePointer of hashmapNodesPointerIterator( | ||
this.carrier, | ||
this.entry.value | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
@@ -86,6 +84,6 @@ yield entryToFinalJavaScriptValue( | ||
for (const nodePointer of hashmapNodesPointerIterator( | ||
this.carrier, | ||
this.entry.value | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer) | ||
)) { | ||
const t = hashMapNodePointerToKeyValue(this.carrier, nodePointer); | ||
const t = hashMapNodePointerToKeyValue(this.carrier.heap, nodePointer); | ||
@@ -117,3 +115,9 @@ yield entryToFinalJavaScriptValue( | ||
return hashMapNodeLookup(this.carrier, this.entry.value, p) !== 0; | ||
return ( | ||
hashMapNodeLookup( | ||
this.carrier.heap, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
) !== 0 | ||
); | ||
} | ||
@@ -130,3 +134,3 @@ | ||
this.carrier, | ||
this.entry.value, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p, | ||
@@ -146,5 +150,4 @@ undefined | ||
return deleteObjectPropertyEntryByKey( | ||
this.externalArgs, | ||
this.carrier, | ||
this.entry.value, | ||
object_pointerToHashMap_get(this.carrier.heap, this.entryPointer), | ||
p | ||
@@ -151,0 +154,0 @@ ); |
@@ -1,206 +0,212 @@ | ||
import { | ||
ExternalArgsApi, | ||
ExternalArgs, | ||
ArrayEntry, | ||
ObjectEntry, | ||
} from "./interfaces"; | ||
import { | ||
externalArgsApiToExternalArgsApi, | ||
isPrimitive, | ||
primitiveValueToEntry, | ||
align, | ||
} from "./utils"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { | ||
LINKED_LIST_MACHINE, | ||
LINKED_LIST_ITEM_MACHINE, | ||
} from "./linkedList/linkedList"; | ||
import { MAP_MACHINE, NODE_MACHINE } from "./hashmap/memoryLayout"; | ||
import { sizeOfEntry } from "./store"; | ||
/* istanbul ignore file */ | ||
export function sizeOf() { | ||
console.warn("FAKE VALUE"); | ||
return Math.pow(2, 24); | ||
} | ||
// import { | ||
// ExternalArgsApi, | ||
// ExternalArgs, | ||
// ArrayEntry, | ||
// ObjectEntry, | ||
// } from "./interfaces"; | ||
// import { | ||
// externalArgsApiToExternalArgsApi, | ||
// isPrimitive, | ||
// primitiveValueToEntry, | ||
// align, | ||
// } from "./utils"; | ||
// import { ENTRY_TYPE } from "./entry-types"; | ||
// import { sizeOfEntry } from "./store"; | ||
// import { | ||
// linkedList_size, | ||
// linkedListItem_size, | ||
// hashmap_size, | ||
// hashmapNode_size, | ||
// } from "./generatedStructs"; | ||
/** | ||
* **UNRELIABLE YET** | ||
* | ||
* Calculate the size (bytes) of the given value. | ||
* Also validates that the value is saveable | ||
*/ | ||
export function sizeOf(externalArgs: ExternalArgsApi, value: any) { | ||
const temp = sizeOfValue( | ||
externalArgsApiToExternalArgsApi(externalArgs), | ||
value | ||
); | ||
// /** | ||
// * **UNRELIABLE YET** | ||
// * | ||
// * Calculate the size (bytes) of the given value. | ||
// * Also validates that the value is saveable | ||
// */ | ||
// export function sizeOf(externalArgs: ExternalArgsApi, value: any) { | ||
// const temp = sizeOfValue( | ||
// externalArgsApiToExternalArgsApi(externalArgs), | ||
// value | ||
// ); | ||
return temp.memoryAllocated + temp.numberOfAllocations * 8; | ||
} | ||
// return temp.memoryAllocated + temp.numberOfAllocations * 8; | ||
// } | ||
interface CheckerResult { | ||
memoryAllocated: number; | ||
numberOfAllocations: number; | ||
} | ||
// interface CheckerResult { | ||
// memoryAllocated: number; | ||
// numberOfAllocations: number; | ||
// } | ||
function sizeOfArray( | ||
externalArgs: ExternalArgs, | ||
arrayToSave: Array<any> | ||
): CheckerResult { | ||
let memoryAllocated = 0; | ||
let numberOfAllocations = 0; | ||
// function sizeOfArray( | ||
// externalArgs: ExternalArgs, | ||
// arrayToSave: Array<any> | ||
// ): CheckerResult { | ||
// let memoryAllocated = 0; | ||
// let numberOfAllocations = 0; | ||
memoryAllocated += | ||
(arrayToSave.length + externalArgs.arrayAdditionalAllocation) * | ||
Uint32Array.BYTES_PER_ELEMENT; | ||
// memoryAllocated += | ||
// (arrayToSave.length + externalArgs.arrayAdditionalAllocation) * | ||
// Uint32Array.BYTES_PER_ELEMENT; | ||
numberOfAllocations += 1; | ||
// numberOfAllocations += 1; | ||
const arrayStartEntry: ArrayEntry = { | ||
type: ENTRY_TYPE.ARRAY, | ||
value: 0, | ||
refsCount: 0, | ||
allocatedLength: | ||
arrayToSave.length + externalArgs.arrayAdditionalAllocation, | ||
length: arrayToSave.length, | ||
}; | ||
// const arrayStartEntry: ArrayEntry = { | ||
// type: ENTRY_TYPE.ARRAY, | ||
// value: 0, | ||
// refsCount: 0, | ||
// allocatedLength: | ||
// arrayToSave.length + externalArgs.arrayAdditionalAllocation, | ||
// length: arrayToSave.length, | ||
// }; | ||
memoryAllocated += align(sizeOfEntry(arrayStartEntry)); | ||
numberOfAllocations += 1; | ||
// memoryAllocated += align(sizeOfEntry(arrayStartEntry)); | ||
// numberOfAllocations += 1; | ||
for (const item of arrayToSave) { | ||
const r = sizeOfValue(externalArgs, item); | ||
// for (const item of arrayToSave) { | ||
// const r = sizeOfValue(externalArgs, item); | ||
memoryAllocated += r.memoryAllocated; | ||
numberOfAllocations += r.numberOfAllocations; | ||
} | ||
// memoryAllocated += r.memoryAllocated; | ||
// numberOfAllocations += r.numberOfAllocations; | ||
// } | ||
return { | ||
memoryAllocated, | ||
numberOfAllocations, | ||
}; | ||
} | ||
// return { | ||
// memoryAllocated, | ||
// numberOfAllocations, | ||
// }; | ||
// } | ||
export function sizeOfObject( | ||
externalArgs: ExternalArgs, | ||
objectToSave: any | ||
): CheckerResult { | ||
let memoryAllocated = 0; | ||
let numberOfAllocations = 0; | ||
// export function sizeOfObject( | ||
// externalArgs: ExternalArgs, | ||
// objectToSave: any | ||
// ): CheckerResult { | ||
// let memoryAllocated = 0; | ||
// let numberOfAllocations = 0; | ||
const objectEntries = Object.entries(objectToSave); | ||
// const objectEntries = Object.entries(objectToSave); | ||
const r = sizeOfHashmap( | ||
externalArgs, | ||
objectEntries.map(([key]) => key) | ||
); | ||
// const r = sizeOfHashmap( | ||
// externalArgs, | ||
// objectEntries.map(([key]) => key) | ||
// ); | ||
memoryAllocated += r.memoryAllocated; | ||
numberOfAllocations += r.allocations; | ||
// memoryAllocated += r.memoryAllocated; | ||
// numberOfAllocations += r.allocations; | ||
for (const [value] of objectEntries) { | ||
const r = sizeOfValue(externalArgs, value); | ||
memoryAllocated += r.memoryAllocated; | ||
numberOfAllocations += r.numberOfAllocations; | ||
} | ||
// for (const [value] of objectEntries) { | ||
// const r = sizeOfValue(externalArgs, value); | ||
// memoryAllocated += r.memoryAllocated; | ||
// numberOfAllocations += r.numberOfAllocations; | ||
// } | ||
const objectStartEntry: ObjectEntry = { | ||
type: ENTRY_TYPE.OBJECT, | ||
refsCount: 0, | ||
value: 0, | ||
}; | ||
// const objectStartEntry: ObjectEntry = { | ||
// type: ENTRY_TYPE.OBJECT, | ||
// refsCount: 0, | ||
// value: 0, | ||
// }; | ||
memoryAllocated += align(sizeOfEntry(objectStartEntry)); | ||
numberOfAllocations += 1; | ||
// memoryAllocated += align(sizeOfEntry(objectStartEntry)); | ||
// numberOfAllocations += 1; | ||
return { | ||
memoryAllocated, | ||
numberOfAllocations, | ||
}; | ||
} | ||
// return { | ||
// memoryAllocated, | ||
// numberOfAllocations, | ||
// }; | ||
// } | ||
export function sizeOfValue( | ||
externalArgs: ExternalArgs, | ||
value: any | ||
): CheckerResult { | ||
if ( | ||
value === undefined || | ||
value === null || | ||
value === true || | ||
value === false | ||
) { | ||
return { | ||
memoryAllocated: 0, | ||
numberOfAllocations: 0, | ||
}; | ||
} | ||
// export function sizeOfValue( | ||
// externalArgs: ExternalArgs, | ||
// value: any | ||
// ): CheckerResult { | ||
// if ( | ||
// value === undefined || | ||
// value === null || | ||
// value === true || | ||
// value === false | ||
// ) { | ||
// return { | ||
// memoryAllocated: 0, | ||
// numberOfAllocations: 0, | ||
// }; | ||
// } | ||
if (isPrimitive(value)) { | ||
const entry = primitiveValueToEntry(value); | ||
// if (isPrimitive(value)) { | ||
// const entry = primitiveValueToEntry(value); | ||
return { | ||
memoryAllocated: align(sizeOfEntry(entry)), | ||
numberOfAllocations: 1, | ||
}; | ||
} else if (Array.isArray(value)) { | ||
return sizeOfArray(externalArgs, value); | ||
} else if (value instanceof Date) { | ||
return { | ||
memoryAllocated: align( | ||
sizeOfEntry({ | ||
type: ENTRY_TYPE.DATE, | ||
refsCount: 0, | ||
value: value.getTime(), | ||
}) | ||
), | ||
numberOfAllocations: 1, | ||
}; | ||
} else if (typeof value === "object") { | ||
return sizeOfObject(externalArgs, value); | ||
} else { | ||
throw new Error("unsupported yet"); | ||
} | ||
} | ||
// return { | ||
// memoryAllocated: align(sizeOfEntry(entry)), | ||
// numberOfAllocations: 1, | ||
// }; | ||
// } else if (Array.isArray(value)) { | ||
// return sizeOfArray(externalArgs, value); | ||
// } else if (value instanceof Date) { | ||
// return { | ||
// memoryAllocated: align( | ||
// sizeOfEntry({ | ||
// type: ENTRY_TYPE.DATE, | ||
// refsCount: 0, | ||
// value: value.getTime(), | ||
// }) | ||
// ), | ||
// numberOfAllocations: 1, | ||
// }; | ||
// } else if (typeof value === "object") { | ||
// return sizeOfObject(externalArgs, value); | ||
// } else { | ||
// throw new Error("unsupported yet"); | ||
// } | ||
// } | ||
// @todo what if rehash will happen on initial insert of value? | ||
function sizeOfHashmap( | ||
externalArgs: ExternalArgs, | ||
keysArray: Array<string | number> | ||
) { | ||
const linkedListBaseAllocationsSize = | ||
align(LINKED_LIST_MACHINE.map.SIZE_OF) + | ||
// end marker | ||
align(LINKED_LIST_ITEM_MACHINE.map.SIZE_OF); | ||
const linkedListBaseAllocations = 2; | ||
// // @todo what if rehash will happen on initial insert of value? | ||
// function sizeOfHashmap( | ||
// externalArgs: ExternalArgs, | ||
// keysArray: Array<string | number> | ||
// ) { | ||
// const linkedListBaseAllocationsSize = | ||
// align(linkedList_size) + | ||
// // end marker | ||
// align(linkedListItem_size); | ||
// const linkedListBaseAllocations = 2; | ||
const linkedListItemsAllocations = keysArray.length; | ||
const linkedListItemsAllocationsSize = | ||
keysArray.length * align(LINKED_LIST_ITEM_MACHINE.map.SIZE_OF); | ||
// const linkedListItemsAllocations = keysArray.length; | ||
// const linkedListItemsAllocationsSize = | ||
// keysArray.length * align(linkedListItem_size); | ||
const hashMapBaseAllocations = 2; | ||
const hashMapBaseAllocationsSize = | ||
align(MAP_MACHINE.map.SIZE_OF) + | ||
align( | ||
externalArgs.hashMapMinInitialCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
); | ||
// const hashMapBaseAllocations = 2; | ||
// const hashMapBaseAllocationsSize = | ||
// align(hashmap_size) + | ||
// align( | ||
// externalArgs.hashMapMinInitialCapacity * Uint32Array.BYTES_PER_ELEMENT | ||
// ); | ||
const hashMapNodesAllocations = keysArray.length; | ||
const hashMapNodesAllocationsSize = | ||
align(NODE_MACHINE.map.SIZE_OF) * keysArray.length; | ||
// const hashMapNodesAllocations = keysArray.length; | ||
// const hashMapNodesAllocationsSize = | ||
// align(hashmapNode_size) * keysArray.length; | ||
const hashMapKeysSize = keysArray | ||
.map((k) => sizeOfEntry(primitiveValueToEntry(k))) | ||
.reduce((p, c) => { | ||
return p + align(c); | ||
}, 0); | ||
// const hashMapKeysSize = keysArray | ||
// .map((k) => sizeOfEntry(primitiveValueToEntry(k))) | ||
// .reduce((p, c) => { | ||
// return p + align(c); | ||
// }, 0); | ||
return { | ||
allocations: | ||
linkedListBaseAllocations + | ||
linkedListItemsAllocations + | ||
hashMapBaseAllocations + | ||
hashMapNodesAllocations + | ||
keysArray.length, | ||
memoryAllocated: | ||
linkedListBaseAllocationsSize + | ||
linkedListItemsAllocationsSize + | ||
hashMapBaseAllocationsSize + | ||
hashMapNodesAllocationsSize + | ||
hashMapKeysSize, | ||
}; | ||
} | ||
// return { | ||
// allocations: | ||
// linkedListBaseAllocations + | ||
// linkedListItemsAllocations + | ||
// hashMapBaseAllocations + | ||
// hashMapNodesAllocations + | ||
// keysArray.length, | ||
// memoryAllocated: | ||
// linkedListBaseAllocationsSize + | ||
// linkedListItemsAllocationsSize + | ||
// hashMapBaseAllocationsSize + | ||
// hashMapNodesAllocationsSize + | ||
// hashMapKeysSize, | ||
// }; | ||
// } |
/* eslint-env jest */ | ||
import { | ||
writeEntry, | ||
initializeArrayBuffer, | ||
readEntry, | ||
appendEntry, | ||
} from "./store"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import * as util from "util"; | ||
import { arrayBuffer2HexArray, makeCarrier } from "./testUtils"; | ||
import { ObjectEntry } from "./interfaces"; | ||
import { externalArgsApiToExternalArgsApi } from "./utils"; | ||
import { initializeArrayBuffer } from "./store"; | ||
import { arrayBuffer2HexArray } from "./testUtils"; | ||
describe("Store tests - Misc", () => { | ||
@@ -47,500 +39,1 @@ test("initializeArrayBuffer", () => { | ||
}); | ||
describe("Store tests writeEntry", () => { | ||
test("writeEntry max number", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 8, { | ||
type: ENTRY_TYPE.NUMBER, | ||
value: Number.MAX_VALUE, | ||
}); | ||
expect(arrayBuffer2HexArray(arrayBuffer.slice(0, 17))) | ||
.toMatchInlineSnapshot(` | ||
Array [ | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x40", | ||
"0xff", | ||
] | ||
`); | ||
}); | ||
test("writeEntry min number", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 8, { | ||
type: ENTRY_TYPE.NUMBER, | ||
value: Number.MIN_VALUE, | ||
}); | ||
expect(arrayBuffer2HexArray(arrayBuffer.slice(0, 17))) | ||
.toMatchInlineSnapshot(` | ||
Array [ | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x40", | ||
"0x01", | ||
] | ||
`); | ||
}); | ||
test("writeEntry string", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 8, { | ||
type: ENTRY_TYPE.STRING, | ||
value: "aא弟", | ||
allocatedBytes: 6, | ||
}); | ||
expect(arrayBuffer2HexArray(arrayBuffer.slice(0, 19))) | ||
.toMatchInlineSnapshot(` | ||
Array [ | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x00", | ||
"0x14", | ||
"0x40", | ||
"0x06", | ||
"0x00", | ||
"0x00", | ||
] | ||
`); | ||
}); | ||
}); | ||
describe("Store tests readEntry", () => { | ||
const externalArgs = externalArgsApiToExternalArgsApi({ | ||
textEncoder: new util.TextEncoder(), | ||
textDecoder: new util.TextDecoder(), | ||
arrayAdditionalAllocation: 20, | ||
}); | ||
test("readEntry max number", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 8, { | ||
type: ENTRY_TYPE.NUMBER, | ||
value: Number.MAX_VALUE, | ||
}); | ||
const redEntry = readEntry(carrier, 8); | ||
// expect(redBytesLength).toBe(writtenLength); | ||
expect(redEntry).toMatchInlineSnapshot(` | ||
Object { | ||
"type": 2, | ||
"value": 1.7976931348623157e+308, | ||
} | ||
`); | ||
}); | ||
test("readEntry min number", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 8, { | ||
type: ENTRY_TYPE.NUMBER, | ||
value: Number.MIN_VALUE, | ||
}); | ||
const redEntry = readEntry(carrier, 8); | ||
expect(redEntry).toMatchInlineSnapshot(` | ||
Object { | ||
"type": 2, | ||
"value": 5e-324, | ||
} | ||
`); | ||
}); | ||
test("readEntry string", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 0, { | ||
type: ENTRY_TYPE.STRING, | ||
value: "aא弟", | ||
allocatedBytes: 6, | ||
}); | ||
const entry = readEntry(carrier, 0); | ||
expect(entry).toMatchInlineSnapshot(` | ||
Object { | ||
"allocatedBytes": 6, | ||
"type": 5, | ||
"value": "aא弟", | ||
} | ||
`); | ||
}); | ||
test("readEntry BigInt", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 0, { | ||
type: ENTRY_TYPE.BIGINT_POSITIVE, | ||
value: BigInt("0b0" + "1".repeat(63)), | ||
}); | ||
const entry = readEntry(carrier, 0); | ||
expect(entry).toMatchInlineSnapshot(` | ||
Object { | ||
"type": 3, | ||
"value": 9223372036854775807n, | ||
} | ||
`); | ||
}); | ||
test("readEntry UBigInt", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 0, { | ||
type: ENTRY_TYPE.BIGINT_POSITIVE, | ||
value: BigInt("0b" + "1".repeat(64)), | ||
}); | ||
const entry = readEntry(carrier, 0); | ||
expect(entry).toMatchInlineSnapshot(` | ||
Object { | ||
"type": 3, | ||
"value": 18446744073709551615n, | ||
} | ||
`); | ||
}); | ||
test("ENTRY_TYPE.BIGINT_POSITIVE max value", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 0, { | ||
type: ENTRY_TYPE.BIGINT_POSITIVE, | ||
value: BigInt("0b" + "1".repeat(64)), | ||
}); | ||
expect(readEntry(carrier, 0)).toMatchInlineSnapshot(` | ||
Object { | ||
"type": 3, | ||
"value": 18446744073709551615n, | ||
} | ||
`); | ||
}); | ||
test("ENTRY_TYPE.BIGINT_NEGATIVE min value", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
writeEntry(carrier, 0, { | ||
type: ENTRY_TYPE.BIGINT_NEGATIVE, | ||
value: -BigInt("0b" + "1".repeat(64)), | ||
}); | ||
expect(readEntry(carrier, 0)).toMatchInlineSnapshot(` | ||
Object { | ||
"type": 4, | ||
"value": -18446744073709551615n, | ||
} | ||
`); | ||
}); | ||
test("BigInt64 overflow error", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
expect(() => { | ||
writeEntry(carrier, 0, { | ||
type: ENTRY_TYPE.BIGINT_POSITIVE, | ||
value: BigInt("0b" + "1".repeat(65)), | ||
}); | ||
}).toThrowErrorMatchingInlineSnapshot(`"BigInt64OverflowError"`); | ||
}); | ||
describe("Store tests write/read entry", () => { | ||
test("object entry", () => { | ||
const arrayBuffer = new ArrayBuffer(64); | ||
const carrier = makeCarrier(arrayBuffer); | ||
const entryToWrite: ObjectEntry = { | ||
type: ENTRY_TYPE.OBJECT, | ||
refsCount: 0, | ||
value: 10, | ||
}; | ||
writeEntry(carrier, 0, entryToWrite); | ||
const entry = readEntry(carrier, 0); | ||
expect(entry).toMatchInlineSnapshot(` | ||
Object { | ||
"refsCount": 0, | ||
"type": 7, | ||
"value": 10, | ||
} | ||
`); | ||
}); | ||
}); | ||
describe("appendEntry - general", () => { | ||
test("appendEntry", () => { | ||
const arrayBuffer = new ArrayBuffer(96); | ||
initializeArrayBuffer(arrayBuffer); | ||
const carrier = makeCarrier(arrayBuffer); | ||
const r1 = appendEntry(externalArgs, carrier, { | ||
type: ENTRY_TYPE.STRING, | ||
value: "im a string", | ||
allocatedBytes: 11, | ||
}); | ||
expect(r1).toMatchInlineSnapshot(`48`); | ||
expect(arrayBuffer2HexArray(arrayBuffer, true)).toMatchInlineSnapshot(` | ||
Array [ | ||
"0:0x00", | ||
"1:0x00", | ||
"2:0x00", | ||
"3:0x00", | ||
"4:0x08", | ||
"5:0x00", | ||
"6:0x00", | ||
"7:0x00", | ||
"8:0x00", | ||
"9:0x00", | ||
"10:0x00", | ||
"11:0x00", | ||
"12:0x00", | ||
"13:0x00", | ||
"14:0x00", | ||
"15:0x00", | ||
"16:0x28", | ||
"17:0x00", | ||
"18:0x00", | ||
"19:0x00", | ||
"20:0x48", | ||
"21:0x00", | ||
"22:0x00", | ||
"23:0x00", | ||
"24:0x60", | ||
"25:0x00", | ||
"26:0x00", | ||
"27:0x00", | ||
"28:0x08", | ||
"29:0x00", | ||
"30:0x00", | ||
"31:0x00", | ||
"32:0x03", | ||
"33:0x00", | ||
"34:0x00", | ||
"35:0x00", | ||
"36:0x10", | ||
"37:0x00", | ||
"38:0x00", | ||
"39:0x00", | ||
"40:0x20", | ||
"41:0x00", | ||
"42:0x00", | ||
"43:0x00", | ||
"44:0x00", | ||
"45:0x00", | ||
"46:0x00", | ||
"47:0x00", | ||
"48:0x00", | ||
"49:0x00", | ||
"50:0x00", | ||
"51:0x00", | ||
"52:0x00", | ||
"53:0x00", | ||
"54:0x14", | ||
"55:0x40", | ||
"56:0x0b", | ||
"57:0x00", | ||
"58:0x00", | ||
"59:0x00", | ||
"60:0x69", | ||
"61:0x6d", | ||
"62:0x20", | ||
"63:0x61", | ||
"64:0x20", | ||
"65:0x73", | ||
"66:0x74", | ||
"67:0x72", | ||
"68:0x69", | ||
"69:0x6e", | ||
"70:0x67", | ||
"71:0x00", | ||
"72:0x00", | ||
"73:0x00", | ||
"74:0x00", | ||
"75:0x00", | ||
"76:0x00", | ||
"77:0x00", | ||
"78:0x00", | ||
"79:0x00", | ||
"80:0x00", | ||
"81:0x00", | ||
"82:0x00", | ||
"83:0x00", | ||
"84:0x00", | ||
"85:0x00", | ||
"86:0x00", | ||
"87:0x00", | ||
"88:0x00", | ||
"89:0x00", | ||
"90:0x00", | ||
"91:0x00", | ||
"92:0x00", | ||
"93:0x00", | ||
"94:0x00", | ||
"95:0x00", | ||
] | ||
`); | ||
expect(arrayBuffer2HexArray(arrayBuffer, true)).toMatchInlineSnapshot(` | ||
Array [ | ||
"0:0x00", | ||
"1:0x00", | ||
"2:0x00", | ||
"3:0x00", | ||
"4:0x08", | ||
"5:0x00", | ||
"6:0x00", | ||
"7:0x00", | ||
"8:0x00", | ||
"9:0x00", | ||
"10:0x00", | ||
"11:0x00", | ||
"12:0x00", | ||
"13:0x00", | ||
"14:0x00", | ||
"15:0x00", | ||
"16:0x28", | ||
"17:0x00", | ||
"18:0x00", | ||
"19:0x00", | ||
"20:0x48", | ||
"21:0x00", | ||
"22:0x00", | ||
"23:0x00", | ||
"24:0x60", | ||
"25:0x00", | ||
"26:0x00", | ||
"27:0x00", | ||
"28:0x08", | ||
"29:0x00", | ||
"30:0x00", | ||
"31:0x00", | ||
"32:0x03", | ||
"33:0x00", | ||
"34:0x00", | ||
"35:0x00", | ||
"36:0x10", | ||
"37:0x00", | ||
"38:0x00", | ||
"39:0x00", | ||
"40:0x20", | ||
"41:0x00", | ||
"42:0x00", | ||
"43:0x00", | ||
"44:0x00", | ||
"45:0x00", | ||
"46:0x00", | ||
"47:0x00", | ||
"48:0x00", | ||
"49:0x00", | ||
"50:0x00", | ||
"51:0x00", | ||
"52:0x00", | ||
"53:0x00", | ||
"54:0x14", | ||
"55:0x40", | ||
"56:0x0b", | ||
"57:0x00", | ||
"58:0x00", | ||
"59:0x00", | ||
"60:0x69", | ||
"61:0x6d", | ||
"62:0x20", | ||
"63:0x61", | ||
"64:0x20", | ||
"65:0x73", | ||
"66:0x74", | ||
"67:0x72", | ||
"68:0x69", | ||
"69:0x6e", | ||
"70:0x67", | ||
"71:0x00", | ||
"72:0x00", | ||
"73:0x00", | ||
"74:0x00", | ||
"75:0x00", | ||
"76:0x00", | ||
"77:0x00", | ||
"78:0x00", | ||
"79:0x00", | ||
"80:0x00", | ||
"81:0x00", | ||
"82:0x00", | ||
"83:0x00", | ||
"84:0x00", | ||
"85:0x00", | ||
"86:0x00", | ||
"87:0x00", | ||
"88:0x00", | ||
"89:0x00", | ||
"90:0x00", | ||
"91:0x00", | ||
"92:0x00", | ||
"93:0x00", | ||
"94:0x00", | ||
"95:0x00", | ||
] | ||
`); | ||
}); | ||
}); | ||
}); |
@@ -1,10 +0,5 @@ | ||
import { ENTRY_TYPE, isPrimitiveEntryType } from "./entry-types"; | ||
import { Entry, primitive, GlobalCarrier } from "./interfaces"; | ||
import { | ||
isPrimitive, | ||
primitiveValueToEntry, | ||
isKnownAddressValuePointer, | ||
} from "./utils"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
import { GlobalCarrier } from "./interfaces"; | ||
import { isKnownAddressValuePointer, isTypeWithRC } from "./utils"; | ||
import { ExternalArgs } from "./interfaces"; | ||
import { BigInt64OverflowError } from "./exceptions"; | ||
import { | ||
@@ -14,9 +9,15 @@ INITIAL_ENTRY_POINTER_TO_POINTER, | ||
} from "./consts"; | ||
import { saveValue } from "./saveValue"; | ||
import { getAllLinkedAddresses } from "./getAllLinkedAddresses"; | ||
import { stringEncodeInto } from "./stringEncodeInto"; | ||
import { stringDecode } from "./stringDecode"; | ||
import { | ||
typeAndRc_refsCount_get, | ||
typeAndRc_refsCount_set, | ||
typeOnly_type_get, | ||
number_value_get, | ||
string_bytesLength_get, | ||
string_charsPointer_get, | ||
} from "./generatedStructs"; | ||
import { Heap } from "../structsGenerator/consts"; | ||
import { readString } from "./readString"; | ||
import { saveValueIterative } from "./saveValue"; | ||
const MAX_64_BIG_INT = BigInt("0xFFFFFFFFFFFFFFFF"); | ||
export function initializeArrayBuffer(arrayBuffer: ArrayBuffer) { | ||
@@ -31,306 +32,70 @@ const uint32 = new Uint32Array(arrayBuffer); | ||
export function sizeOfEntry(entry: Entry) { | ||
let cursor = 0; | ||
// /* istanbul ignore next */ | ||
// export function sizeOfEntry(entry: Entry) { | ||
// let cursor = 0; | ||
// type | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// // type | ||
// cursor += Float64Array.BYTES_PER_ELEMENT; | ||
switch (entry.type) { | ||
case ENTRY_TYPE.NUMBER: | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
break; | ||
// switch (entry.type) { | ||
// case ENTRY_TYPE.NUMBER: | ||
// cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// break; | ||
case ENTRY_TYPE.STRING: | ||
// string length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// case ENTRY_TYPE.STRING: | ||
// // string length | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
cursor += entry.allocatedBytes; | ||
// cursor += entry.allocatedBytes; | ||
// oh boy. i don't want to change it now, but no choice | ||
// @todo: this is incorrect? should be Math.max | ||
// cursor += entry.allocatedBytes; | ||
// // oh boy. i don't want to change it now, but no choice | ||
// // @todo: this is incorrect? should be Math.max | ||
// // cursor += entry.allocatedBytes; | ||
break; | ||
// break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
if (entry.value > MAX_64_BIG_INT || entry.value < -MAX_64_BIG_INT) { | ||
throw new BigInt64OverflowError(); | ||
} | ||
// case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
// case ENTRY_TYPE.BIGINT_POSITIVE: | ||
// if (entry.value > MAX_64_BIG_INT || entry.value < -MAX_64_BIG_INT) { | ||
// throw new BigInt64OverflowError(); | ||
// } | ||
cursor += BigInt64Array.BYTES_PER_ELEMENT; | ||
break; | ||
// cursor += BigInt64Array.BYTES_PER_ELEMENT; | ||
// break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.MAP: | ||
case ENTRY_TYPE.SET: | ||
// ref count | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// pointer | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
// case ENTRY_TYPE.OBJECT: | ||
// case ENTRY_TYPE.MAP: | ||
// case ENTRY_TYPE.SET: | ||
// // ref count | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // pointer | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// break; | ||
case ENTRY_TYPE.ARRAY: | ||
// refsCount | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// pointer | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// allocated length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
// case ENTRY_TYPE.ARRAY: | ||
// // refsCount | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // pointer | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // length | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// // allocated length | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// break; | ||
case ENTRY_TYPE.DATE: | ||
// timestamp | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// ref count | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
// case ENTRY_TYPE.DATE: | ||
// // timestamp | ||
// cursor += Float64Array.BYTES_PER_ELEMENT; | ||
// // ref count | ||
// cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// break; | ||
default: | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
} | ||
// default: | ||
// // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
// } | ||
return cursor; | ||
} | ||
// return cursor; | ||
// } | ||
export function writeEntry( | ||
carrier: GlobalCarrier, | ||
startingCursor: number, | ||
entry: Entry | ||
) { | ||
let cursor = startingCursor; | ||
// let writtenDataSizeInBytes = 0; | ||
// write type | ||
// undo on throw ? | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT] = entry.type; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
switch (entry.type) { | ||
case ENTRY_TYPE.NUMBER: | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.STRING: | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = | ||
entry.allocatedBytes; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// eslint-disable-next-line no-case-declarations | ||
const writtenBytes = stringEncodeInto(carrier.uint8, cursor, entry.value); | ||
if (writtenBytes !== entry.allocatedBytes) { | ||
// eslint-disable-next-line no-undef | ||
console.warn( | ||
{ | ||
value: entry.value, | ||
writtenBytes, | ||
allocatedBytes: entry.allocatedBytes, | ||
}, | ||
true | ||
); | ||
throw new Error("WTF???"); | ||
} | ||
cursor += entry.allocatedBytes; | ||
break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
if (entry.value > MAX_64_BIG_INT || entry.value < -MAX_64_BIG_INT) { | ||
throw new BigInt64OverflowError(); | ||
} | ||
carrier.bigUint64[cursor / BigUint64Array.BYTES_PER_ELEMENT] = | ||
entry.type === ENTRY_TYPE.BIGINT_NEGATIVE ? -entry.value : entry.value; | ||
cursor += BigUint64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.SET: | ||
case ENTRY_TYPE.MAP: | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.refsCount; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.ARRAY: | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.refsCount; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.length; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = | ||
entry.allocatedLength; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.DATE: | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT] = entry.value; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT] = entry.refsCount; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
default: | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
throw new Error(ENTRY_TYPE[entry.type] + " Not implemented yet"); | ||
} | ||
} | ||
export function appendEntry( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
entry: Entry | ||
) { | ||
const size = sizeOfEntry(entry); | ||
const memoryAddress = carrier.allocator.calloc(size); | ||
writeEntry(carrier, memoryAddress, entry); | ||
return memoryAddress; | ||
} | ||
export function readEntry( | ||
carrier: GlobalCarrier, | ||
startingCursor: number | ||
): Entry { | ||
let cursor = startingCursor; | ||
const entryType: ENTRY_TYPE = | ||
carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
const entry: any = { | ||
type: entryType, | ||
value: undefined as any, | ||
}; | ||
// let writtenDataSizeInBytes = 0; | ||
switch (entryType) { | ||
// handled by well-known addresses | ||
// case ENTRY_TYPE.UNDEFINED: | ||
// break; | ||
// case ENTRY_TYPE.NULL: | ||
// break; | ||
// case ENTRY_TYPE.BOOLEAN: | ||
// entry.value = carrier.dataView.getUint8(cursor) !== 0; | ||
// cursor += Uint8Array.BYTES_PER_ELEMENT; | ||
// break; | ||
case ENTRY_TYPE.NUMBER: | ||
entry.value = carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.STRING: | ||
// eslint-disable-next-line no-case-declarations | ||
const stringLength = | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
entry.allocatedBytes = stringLength; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
// decode fails with zero length array | ||
if (stringLength > 0) { | ||
// this wrapping is needed until: | ||
// https://github.com/whatwg/encoding/issues/172 | ||
// eslint-disable-next-line no-case-declarations | ||
// const tempAB = new ArrayBuffer(stringLength, true); | ||
// arrayBufferCopyTo(dataView.buffer, cursor, stringLength, tempAB, 0, true); | ||
entry.value = stringDecode(carrier.uint8, cursor, stringLength); | ||
} else { | ||
entry.value = ""; | ||
} | ||
cursor += stringLength; | ||
break; | ||
case ENTRY_TYPE.BIGINT_POSITIVE: | ||
entry.value = | ||
carrier.bigUint64[cursor / BigUint64Array.BYTES_PER_ELEMENT]; | ||
cursor += BigUint64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.BIGINT_NEGATIVE: | ||
entry.value = -carrier.bigUint64[ | ||
cursor / BigUint64Array.BYTES_PER_ELEMENT | ||
]; | ||
cursor += BigUint64Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.OBJECT: | ||
case ENTRY_TYPE.MAP: | ||
case ENTRY_TYPE.SET: | ||
entry.refsCount = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.value = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.ARRAY: | ||
entry.refsCount = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.value = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.length = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
entry.allocatedLength = | ||
carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
case ENTRY_TYPE.DATE: | ||
entry.value = carrier.float64[cursor / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
entry.refsCount = carrier.uint32[cursor / Uint32Array.BYTES_PER_ELEMENT]; | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
break; | ||
default: | ||
throw new Error(ENTRY_TYPE[entryType] + " Not implemented yet"); | ||
} | ||
return entry; | ||
} | ||
export function canReuseMemoryOfEntry(entryA: Entry, value: primitive) { | ||
const typeofTheValue = typeof value; | ||
// number & bigint 64 are the same size | ||
if ( | ||
(entryA.type === ENTRY_TYPE.BIGINT_NEGATIVE || | ||
entryA.type === ENTRY_TYPE.BIGINT_POSITIVE || | ||
entryA.type === ENTRY_TYPE.NUMBER) && | ||
(typeofTheValue === "bigint" || typeofTheValue === "number") | ||
) { | ||
return true; | ||
} | ||
// kill for strings for now | ||
// if ( | ||
// entryA.type === ENTRY_TYPE.STRING && | ||
// typeofTheValue === "string" && | ||
// entryA.allocatedBytes >= strByteLength(value as string) | ||
// ) { | ||
// return true; | ||
// } | ||
return false; | ||
} | ||
export function writeValueInPtrToPtr( | ||
@@ -340,40 +105,15 @@ externalArgs: ExternalArgs, | ||
ptrToPtr: number, | ||
value: any | ||
value: unknown | ||
) { | ||
const existingEntryPointer = | ||
carrier.uint32[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
const referencedPointers: number[] = []; | ||
// Might oom here | ||
saveValueIterative( | ||
externalArgs, | ||
carrier, | ||
referencedPointers, | ||
ptrToPtr, | ||
value | ||
); | ||
let reuse = false; | ||
let existingValueEntry: Entry | undefined; | ||
if (!isKnownAddressValuePointer(existingEntryPointer)) { | ||
existingValueEntry = readEntry(carrier, existingEntryPointer); | ||
reuse = | ||
isPrimitive(value) && | ||
isPrimitiveEntryType(existingValueEntry.type) && | ||
canReuseMemoryOfEntry(existingValueEntry, value); | ||
} | ||
// try to re use memory | ||
if (reuse) { | ||
const newEntry = primitiveValueToEntry(value); | ||
writeEntry(carrier, existingEntryPointer, newEntry); | ||
} else { | ||
const referencedPointers: number[] = []; | ||
const newEntryPointer = saveValue( | ||
externalArgs, | ||
carrier, | ||
referencedPointers, | ||
value | ||
); | ||
carrier.uint32[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT] = newEntryPointer; | ||
return { | ||
referencedPointers, | ||
existingEntryPointer, | ||
existingValueEntry, | ||
}; | ||
} | ||
return referencedPointers; | ||
} | ||
@@ -385,48 +125,30 @@ | ||
ptrToPtr: number, | ||
value: any | ||
value: unknown | ||
) { | ||
const { | ||
existingValueEntry = false, | ||
existingEntryPointer = 0, | ||
referencedPointers = [], | ||
} = writeValueInPtrToPtr(externalArgs, carrier, ptrToPtr, value) || {}; | ||
const existingEntryPointer = | ||
carrier.heap.Uint32Array[ptrToPtr / Uint32Array.BYTES_PER_ELEMENT]; | ||
// Might oom here | ||
const referencedPointers = writeValueInPtrToPtr( | ||
externalArgs, | ||
carrier, | ||
ptrToPtr, | ||
value | ||
); | ||
// -- end of might oom | ||
// commit ref count changes of existing objects | ||
if (referencedPointers.length > 0) { | ||
for (const ptr of referencedPointers) { | ||
incrementRefCount(externalArgs, carrier, ptr); | ||
incrementRefCount(carrier.heap, ptr); | ||
} | ||
} | ||
if (existingValueEntry && "refsCount" in existingValueEntry) { | ||
const newRefCount = decrementRefCount( | ||
externalArgs, | ||
carrier, | ||
existingEntryPointer | ||
); | ||
if (newRefCount === 0) { | ||
const addressesToFree = getAllLinkedAddresses( | ||
carrier, | ||
false, | ||
existingEntryPointer | ||
); | ||
for (const address of addressesToFree.leafAddresses) { | ||
carrier.allocator.free(address); | ||
} | ||
for (const address of addressesToFree.arcAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
} | ||
} | ||
} else { | ||
carrier.allocator.free(existingEntryPointer); | ||
} | ||
handleArcForDeletedValuePointer(carrier, existingEntryPointer); | ||
} | ||
export function handleArcForDeletedValuePointer( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
deletedValuePointer: number | ||
): void { | ||
const { heap, allocator } = carrier; | ||
// No memory to free/ARC | ||
@@ -437,106 +159,53 @@ if (isKnownAddressValuePointer(deletedValuePointer)) { | ||
const existingValueEntry = readEntry(carrier, deletedValuePointer); | ||
if (existingValueEntry && "refsCount" in existingValueEntry) { | ||
const newRefCount = decrementRefCount( | ||
externalArgs, | ||
carrier, | ||
deletedValuePointer | ||
); | ||
if (newRefCount === 0) { | ||
const addressesToFree = getAllLinkedAddresses( | ||
carrier, | ||
false, | ||
deletedValuePointer | ||
const entryType = typeOnly_type_get(heap, deletedValuePointer); | ||
if (!isTypeWithRC(entryType)) { | ||
if (entryType === ENTRY_TYPE.STRING) { | ||
allocator.free( | ||
string_charsPointer_get(carrier.heap, deletedValuePointer) | ||
); | ||
for (const address of addressesToFree.leafAddresses) { | ||
carrier.allocator.free(address); | ||
} | ||
for (const address of addressesToFree.arcAddresses) { | ||
decrementRefCount(externalArgs, carrier, address); | ||
} | ||
} | ||
} else { | ||
carrier.allocator.free(deletedValuePointer); | ||
allocator.free(deletedValuePointer); | ||
return; | ||
} | ||
} | ||
export function incrementRefCount( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
entryPointer: number | ||
) { | ||
const entry = readEntry(carrier, entryPointer); | ||
if (decrementRefCount(heap, deletedValuePointer) > 0) { | ||
allocator.free(deletedValuePointer); | ||
return; | ||
} | ||
if ("refsCount" in entry) { | ||
entry.refsCount += 1; | ||
writeEntry(carrier, entryPointer, entry); | ||
const { leafAddresses, arcAddresses } = getAllLinkedAddresses( | ||
carrier.heap, | ||
false, | ||
deletedValuePointer | ||
); | ||
return entry.refsCount; | ||
for (const address of leafAddresses) { | ||
allocator.free(address); | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function decrementRefCount( | ||
externalArgs: ExternalArgs, | ||
carrier: GlobalCarrier, | ||
entryPointer: number | ||
) { | ||
const entry = readEntry(carrier, entryPointer); | ||
if ("refsCount" in entry) { | ||
entry.refsCount -= 1; | ||
writeEntry(carrier, entryPointer, entry); | ||
return entry.refsCount; | ||
for (const address of arcAddresses) { | ||
decrementRefCount(heap, address); | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function getRefCount(carrier: GlobalCarrier, entryPointer: number) { | ||
const entry = readEntry(carrier, entryPointer); | ||
export function incrementRefCount(heap: Heap, entryPointer: number) { | ||
typeAndRc_refsCount_set( | ||
heap, | ||
entryPointer, | ||
typeAndRc_refsCount_get(heap, entryPointer) + 1 | ||
); | ||
if ("refsCount" in entry) { | ||
return entry.refsCount; | ||
} | ||
throw new Error("unexpected"); | ||
return typeAndRc_refsCount_get(heap, entryPointer); | ||
} | ||
export function setRefCount( | ||
carrier: GlobalCarrier, | ||
entryPointer: number, | ||
newRefCount: number | ||
) { | ||
const entry = readEntry(carrier, entryPointer); | ||
export function decrementRefCount(heap: Heap, entryPointer: number) { | ||
typeAndRc_refsCount_set( | ||
heap, | ||
entryPointer, | ||
typeAndRc_refsCount_get(heap, entryPointer) - 1 | ||
); | ||
if ("refsCount" in entry) { | ||
const prevCount = entry.refsCount; | ||
entry.refsCount = newRefCount; | ||
writeEntry(carrier, entryPointer, entry); | ||
return prevCount; | ||
} | ||
throw new Error("unexpected"); | ||
return typeAndRc_refsCount_get(heap, entryPointer); | ||
} | ||
// export function getObjectPropPtrToPtr( | ||
// { dataView }: GlobalCarrier, | ||
// pointerToEntry: number | ||
// ) { | ||
// const keyStringLength = dataView.getUint16(pointerToEntry + 1); | ||
// const valuePtrToPtr = | ||
// Uint16Array.BYTES_PER_ELEMENT + pointerToEntry + 1 + keyStringLength; | ||
// const nextPtrToPtr = valuePtrToPtr + Uint32Array.BYTES_PER_ELEMENT; | ||
// return { | ||
// valuePtrToPtr, | ||
// nextPtrToPtr | ||
// }; | ||
// } | ||
export function getObjectValuePtrToPtr(pointerToEntry: number) { | ||
@@ -569,13 +238,17 @@ return pointerToEntry + 1 + 1; | ||
export function compareStringOrNumberEntriesInPlace( | ||
carrier: GlobalCarrier, | ||
heap: Heap, | ||
entryAPointer: number, | ||
entryBPointer: number | ||
) { | ||
let cursor = 0; | ||
const entryAType: ENTRY_TYPE = | ||
carrier.float64[(entryAPointer + cursor) / Float64Array.BYTES_PER_ELEMENT]; | ||
const entryBType: ENTRY_TYPE = | ||
carrier.float64[(entryBPointer + cursor) / Float64Array.BYTES_PER_ELEMENT]; | ||
cursor += Float64Array.BYTES_PER_ELEMENT; | ||
typeOnly_type_get(heap, entryAPointer); | ||
const entryAType: ENTRY_TYPE.STRING | ENTRY_TYPE.NUMBER = typeOnly_type_get( | ||
heap, | ||
entryAPointer | ||
); | ||
const entryBType: ENTRY_TYPE.STRING | ENTRY_TYPE.NUMBER = typeOnly_type_get( | ||
heap, | ||
entryBPointer | ||
); | ||
if (entryAType !== entryBType) { | ||
@@ -586,6 +259,4 @@ return false; | ||
if (entryAType === ENTRY_TYPE.STRING) { | ||
const aLength = | ||
carrier.uint32[(entryAPointer + cursor) / Uint32Array.BYTES_PER_ELEMENT]; | ||
const bLength = | ||
carrier.uint32[(entryBPointer + cursor) / Uint32Array.BYTES_PER_ELEMENT]; | ||
const aLength = string_bytesLength_get(heap, entryAPointer); | ||
const bLength = string_bytesLength_get(heap, entryBPointer); | ||
@@ -596,9 +267,6 @@ if (aLength !== bLength) { | ||
// string length | ||
cursor += Uint32Array.BYTES_PER_ELEMENT; | ||
return memComp( | ||
carrier.uint8, | ||
entryAPointer + cursor, | ||
entryBPointer + cursor, | ||
heap.Uint8Array, | ||
string_charsPointer_get(heap, entryAPointer), | ||
string_charsPointer_get(heap, entryBPointer), | ||
aLength | ||
@@ -608,8 +276,20 @@ ); | ||
// numbers | ||
return ( | ||
carrier.float64[ | ||
(entryAPointer + cursor) / Float64Array.BYTES_PER_ELEMENT | ||
] === | ||
carrier.float64[(entryBPointer + cursor) / Float64Array.BYTES_PER_ELEMENT] | ||
number_value_get(heap, entryAPointer) === | ||
number_value_get(heap, entryBPointer) | ||
); | ||
} | ||
export function readNumberOrString(heap: Heap, pointer: number) { | ||
const type: ENTRY_TYPE.NUMBER | ENTRY_TYPE.STRING = typeOnly_type_get( | ||
heap, | ||
pointer | ||
); | ||
if (type === ENTRY_TYPE.NUMBER) { | ||
return number_value_get(heap, pointer); | ||
} else { | ||
return readString(heap, pointer); | ||
} | ||
} |
@@ -0,1 +1,2 @@ | ||
/* istanbul ignore file */ | ||
// https://github.com/anonyco/FastestSmallestTextEncoderDecoder/blob/master/EncoderDecoderTogether.src.js | ||
@@ -2,0 +3,0 @@ // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder#Polyfill |
@@ -27,2 +27,3 @@ export function arrayBuffer2HexArray( | ||
import { MEM_POOL_START } from "./consts"; | ||
import { createHeap } from "../structsGenerator/consts"; | ||
@@ -81,4 +82,4 @@ // extend pool and not monkey patch? need to think about it | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
pool.free = function free(ptr: number) { | ||
@@ -107,9 +108,4 @@ deallocations.add(ptr); | ||
const carrier: GlobalCarrier = { | ||
// dataView: new DataView(arrayBuffer), | ||
allocator, | ||
uint8: new Uint8Array(arrayBuffer), | ||
uint16: new Uint16Array(arrayBuffer), | ||
uint32: new Uint32Array(arrayBuffer), | ||
float64: new Float64Array(arrayBuffer), | ||
bigUint64: new BigUint64Array(arrayBuffer), | ||
heap: createHeap(arrayBuffer), | ||
}; | ||
@@ -116,0 +112,0 @@ |
@@ -1,8 +0,2 @@ | ||
import { | ||
primitive, | ||
Entry, | ||
ExternalArgs, | ||
InternalAPI, | ||
ExternalArgsApi, | ||
} from "./interfaces"; | ||
import { ExternalArgs, InternalAPI, ExternalArgsApi } from "./interfaces"; | ||
import { ENTRY_TYPE } from "./entry-types"; | ||
@@ -18,51 +12,2 @@ import { INTERNAL_API_SYMBOL } from "./symbols"; | ||
const primitives = [ | ||
"string", | ||
"number", | ||
"bigint", | ||
"boolean", | ||
"undefined", | ||
] as const; | ||
export function isPrimitive(value: unknown): value is primitive { | ||
if (primitives.includes(typeof value as any)) { | ||
return true; | ||
} | ||
if (value === null) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
export function primitiveValueToEntry(value: primitive): Entry { | ||
if (typeof value === "string") { | ||
return { | ||
type: ENTRY_TYPE.STRING, | ||
value, | ||
allocatedBytes: strByteLength(value), | ||
}; | ||
} | ||
if (typeof value === "number") { | ||
return { | ||
type: ENTRY_TYPE.NUMBER, | ||
value, | ||
}; | ||
} | ||
if (typeof value === "bigint") { | ||
return { | ||
type: | ||
value >= BigInt("0") | ||
? ENTRY_TYPE.BIGINT_POSITIVE | ||
: ENTRY_TYPE.BIGINT_NEGATIVE, | ||
value, | ||
}; | ||
} | ||
throw new Error("unexpected"); | ||
} | ||
export function createKnownTypeGuard<T>(arr: ReadonlyArray<T>) { | ||
@@ -125,2 +70,6 @@ return function knownTypeGuard(v: unknown): v is T { | ||
/** | ||
* Incorrect length (too big) for emojis | ||
* @param str | ||
*/ | ||
export function strByteLength(str: string) { | ||
@@ -149,1 +98,28 @@ let s = str.length; | ||
} | ||
export function isTypeWithRC(type: ENTRY_TYPE) { | ||
return ( | ||
type === ENTRY_TYPE.OBJECT || | ||
type === ENTRY_TYPE.ARRAY || | ||
type === ENTRY_TYPE.DATE || | ||
type === ENTRY_TYPE.MAP || | ||
type === ENTRY_TYPE.SET | ||
); | ||
} | ||
/** | ||
* | ||
* I hope It's reliable | ||
*/ | ||
export function isSupportedTopLevelValue(value: unknown) { | ||
return !( | ||
Array.isArray(value) || | ||
value instanceof Date || | ||
value instanceof Map || | ||
value instanceof Set || | ||
typeof value !== "object" || | ||
typeof value === null || | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
value!.constructor.name !== "Object" | ||
); | ||
} |
@@ -0,1 +1,6 @@ | ||
/* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
/* istanbul ignore file */ | ||
// We can't run test with weakrefs yet | ||
const KEYS = 1; | ||
@@ -30,10 +35,23 @@ const VALUES = 2; | ||
this.group = new FinalizationSomething((iterator: Iterable<any>) => { | ||
for (const key of iterator) { | ||
this.map.delete(key); | ||
if (this.externalFinalizer) { | ||
this.externalFinalizer(key); | ||
this.group = new FinalizationSomething( | ||
(iteratorOrKey: Iterable<unknown> | unknown) => { | ||
// @ts-expect-error | ||
if (Symbol.iterator in iterable) { | ||
// @ts-expect-error | ||
for (const key of iteratorOrKey) { | ||
this.map.delete(key); | ||
if (this.externalFinalizer) { | ||
this.externalFinalizer(key); | ||
} | ||
} | ||
} else { | ||
// @ts-expect-error | ||
this.map.delete(iteratorOrKey); | ||
if (this.externalFinalizer) { | ||
// @ts-expect-error | ||
this.externalFinalizer(iteratorOrKey); | ||
} | ||
} | ||
} | ||
}); | ||
); | ||
} | ||
@@ -40,0 +58,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
1276880
205
119
37
15922
1
Updated@thi.ng/malloc@^4.1.14