Skip to content

Commit

Permalink
Merge 29d9f4d into fe355f6
Browse files Browse the repository at this point in the history
  • Loading branch information
Bnaya committed Jun 20, 2020
2 parents fe355f6 + 29d9f4d commit b67f7b3
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 65 deletions.
60 changes: 60 additions & 0 deletions src/internal/hashmap/hashFunctionsStuff.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint-env jest */

import { hashString, hashUint8CodeInPlace } from "./hashFunctionsStuff";
import { stringEncodeInto } from "../stringEncodeInto";
import { stringLengthV2 } from "../stringLengthV2";

function prepHashes(str: string) {
const uint8 = new Uint8Array(stringLengthV2(str));
stringEncodeInto(uint8, 0, str);

const uint8Hash = hashUint8CodeInPlace(uint8, 0, uint8.byteLength);
const flatHash = hashString(str);

return {
uint8Hash,
flatHash,
};
}

describe("hashCodeExternalValue", () => {
test("simple string comparison", () => {
const str = "a23452345";

const { uint8Hash, flatHash } = prepHashes(str);

expect(flatHash).toBe(uint8Hash);

expect(flatHash).toMatchInlineSnapshot(`1568997221`);
});

test("simple string comparison 3", () => {
const str = "myDate";

const { uint8Hash, flatHash } = prepHashes(str);

expect(flatHash).toBe(uint8Hash);

expect(flatHash).toMatchInlineSnapshot(`1060521094`);
});

test("simple string comparison 3", () => {
const str = "bigintNegative";

const { uint8Hash, flatHash } = prepHashes(str);

expect(flatHash).toBe(uint8Hash);

expect(flatHash).toMatchInlineSnapshot(`700576700`);
});

test("simple string comparison 4", () => {
const str = "bigintPositive";

const { uint8Hash, flatHash } = prepHashes(str);

expect(flatHash).toBe(uint8Hash);

expect(flatHash).toMatchInlineSnapshot(`873883128`);
});
});
100 changes: 100 additions & 0 deletions src/internal/hashmap/hashFunctionsStuff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
export function hashCodeExternalValue(value: string | number): number {
if (typeof value === "number") {
return hashNumber(value);
}

return hashString(value);
}

/**
* We may replace that with code that knows to break down float into bytes
*/
const helperFloatArray = new Float64Array(1);
const helperUint8Array = new Uint8Array(helperFloatArray.buffer);

export function hashNumber(num: number) {
helperFloatArray[0] = num;

return hashUint8CodeInPlace(helperUint8Array, 0, helperUint8Array.byteLength);
}

export function hashUint8CodeInPlace(
uint8: Uint8Array,
keyStart: number,
keyBytesLength: number
): number {
let h = 0 | 0;

// const hashed: number[] = [];

for (let i = 0; i < keyBytesLength; i++) {
// h = (Math.imul(31, h) + uint8[i + keyStart]) | 0;
h = hashStep(h, uint8[i + keyStart]);
}

// console.log(hashed);

return Math.abs(h);
}

function hashStep(h: number, v: number) {
// console.log({ h, v });
return (Math.imul(31, h) + v) | 0;
}

export function hashString(str: string) {
const strLen = str.length;
let h = 0 | 0;

for (let point = 0, nextCode = 0, i = 0; i !== strLen; ) {
(point = str.charCodeAt(i)), (i += 1);

if (point >= 0xd800 && point <= 0xdbff) {
if (i === strLen) {
h = hashStep(h, 0xef) /*0b11101111*/;
h = hashStep(h, 0xbf) /*0b10111111*/;
h = hashStep(h, 0xbd) /*0b10111101*/;
break;
}
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
nextCode = str.charCodeAt(i);
if (nextCode >= 0xdc00 && nextCode <= 0xdfff) {
point = (point - 0xd800) * 0x400 + nextCode - 0xdc00 + 0x10000;
i += 1;
if (point > 0xffff) {
h = hashStep(h, (0x1e /*0b11110*/ << 3) | (point >>> 18));
h = hashStep(
h,
(0x2 /*0b10*/ << 6) | ((point >>> 12) & 0x3f)
) /*0b00111111*/;
h = hashStep(
h,
(0x2 /*0b10*/ << 6) | ((point >>> 6) & 0x3f)
) /*0b00111111*/;
h = hashStep(h, (0x2 /*0b10*/ << 6) | (point & 0x3f)) /*0b00111111*/;
continue;
}
} else {
h = hashStep(h, 0xef) /*0b11101111*/;
h = hashStep(h, 0xbf) /*0b10111111*/;
h = hashStep(h, 0xbd) /*0b10111101*/;
continue;
}
}
if (point <= 0x007f) {
h = hashStep(h, (0x0 /*0b0*/ << 7) | point);
} else if (point <= 0x07ff) {
h = hashStep(h, (0x6 /*0b110*/ << 5) | (point >>> 6));
h = hashStep(h, (0x2 /*0b10*/ << 6) | (point & 0x3f)) /*0b00111111*/;
} else {
h = hashStep(h, (0xe /*0b1110*/ << 4) | (point >>> 12));
h = hashStep(
h,
(0x2 /*0b10*/ << 6) | ((point >>> 6) & 0x3f)
) /*0b00111111*/;
h = hashStep(h, (0x2 /*0b10*/ << 6) | (point & 0x3f)) /*0b00111111*/;
}
}

return Math.abs(h);
}
54 changes: 26 additions & 28 deletions src/internal/hashmap/hashmap.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { GlobalCarrier, ExternalArgs } from "../interfaces";
import {
hashCodeInPlace,
hashCodeExternalValue,
getKeyStart,
getKeyLength,
} from "./hashmapUtils";
import { getKeyStart, getKeyLength } from "./hashmapUtils";
import { stringEncodeInto } from "../stringEncodeInto";
import {
compareStringOrNumberEntriesInPlace,
Expand Down Expand Up @@ -53,6 +48,10 @@ import {
} from "../generatedStructs";
import { Heap } from "../../structsGenerator/consts";
import { stringLengthV2 } from "../stringLengthV2";
import {
hashUint8CodeInPlace,
hashCodeExternalValue,
} from "./hashFunctionsStuff";

export function createHashMap(
carrier: GlobalCarrier,
Expand Down Expand Up @@ -108,12 +107,12 @@ export function hashMapInsertUpdateKeyIsPointerReturnNode(
keyDataMemoryStart = string_charsPointer_get(heap, keyPointer);
}

const bucket = hashCodeInPlace(
heap.Uint8Array,
hashmap_CAPACITY_get(heap, mapPointer),
keyDataMemoryStart,
keyDataMemoryLength
);
const bucket =
hashUint8CodeInPlace(
heap.Uint8Array,
keyDataMemoryStart,
keyDataMemoryLength
) % hashmap_CAPACITY_get(heap, mapPointer);

const bucketStartPointer =
hashmap_ARRAY_POINTER_get(heap, mapPointer) +
Expand Down Expand Up @@ -246,12 +245,12 @@ export function hashMapInsertUpdate(
);
}

const bucket = hashCodeInPlace(
heap.Uint8Array,
hashmap_CAPACITY_get(heap, mapPointer),
keyDataMemoryStart,
keyDataMemoryLength
);
const bucket =
hashUint8CodeInPlace(
heap.Uint8Array,
keyDataMemoryStart,
keyDataMemoryLength
) % hashmap_CAPACITY_get(heap, mapPointer);

const bucketStartPointer =
hashmap_ARRAY_POINTER_get(heap, mapPointer) +
Expand Down Expand Up @@ -357,10 +356,9 @@ export function hashMapNodeLookup(
mapPointer: number,
externalKeyValue: number | string
) {
const bucket = hashCodeExternalValue(
hashmap_CAPACITY_get(heap, mapPointer),
externalKeyValue
);
const bucket =
hashCodeExternalValue(externalKeyValue) %
hashmap_CAPACITY_get(heap, mapPointer);

const bucketStartPtrToPtr =
hashmap_ARRAY_POINTER_get(heap, mapPointer) +
Expand Down Expand Up @@ -575,12 +573,12 @@ function hashMapRehashInsert(
hashmapPointer: number,
nodePointer: number
) {
const bucket = hashCodeInPlace(
heap.Uint8Array,
hashmap_CAPACITY_get(heap, hashmapPointer),
getKeyStart(heap, hashmapNode_KEY_POINTER_get(heap, nodePointer)),
getKeyLength(heap, hashmapNode_KEY_POINTER_get(heap, nodePointer))
);
const bucket =
hashUint8CodeInPlace(
heap.Uint8Array,
getKeyStart(heap, hashmapNode_KEY_POINTER_get(heap, nodePointer)),
getKeyLength(heap, hashmapNode_KEY_POINTER_get(heap, nodePointer))
) % hashmap_CAPACITY_get(heap, hashmapPointer);

const bucketStartPointer =
hashmap_ARRAY_POINTER_get(heap, hashmapPointer) +
Expand Down
37 changes: 0 additions & 37 deletions src/internal/hashmap/hashmapUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ENTRY_TYPE } from "../entry-types";
import { stringEncodeInto } from "../stringEncodeInto";
import { Heap } from "../../structsGenerator/consts";
import {
number_value_place,
Expand All @@ -9,42 +8,6 @@ import {
string_bytesLength_get,
} from "../generatedStructs";

export function hashCodeInPlace(
uint8: Uint8Array,
capacity: number,
keyStart: number,
keyBytesLength: number
): number {
let h = 0 | 0;

// const hashed: number[] = [];

for (let i = 0; i < keyBytesLength; i++) {
h = (Math.imul(31, h) + uint8[i + keyStart]) | 0;
}

// console.log(hashed);

return Math.abs(h % capacity);
}

export function hashCodeExternalValue(
capacity: number,
value: string | number
): number {
const ab = new ArrayBuffer(typeof value === "string" ? value.length * 3 : 8);
const uint8 = new Uint8Array(ab);
let keyBytesLength = ab.byteLength;

if (typeof value === "string") {
keyBytesLength = stringEncodeInto(new Uint8Array(ab), 0, value);
} else {
new Float64Array(ab)[0] = value;
}

return hashCodeInPlace(uint8, capacity, 0, keyBytesLength);
}

export function getKeyStart(heap: Heap, keyPointer: number) {
if (typeOnly_type_get(heap, keyPointer) === ENTRY_TYPE.NUMBER) {
return keyPointer + number_value_place;
Expand Down

0 comments on commit b67f7b3

Please sign in to comment.