UNPKG

reign

Version:

A persistent, typed-objects implementation.

255 lines (227 loc) 7.26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.STRING_DATA_OFFSET = exports.STRING_HEADER_SIZE = exports.STRING_HASH_OFFSET = exports.STRING_LENGTH_OFFSET = undefined; exports.make = make; var _performance = require("../../performance"); var _string = require("../../random/string"); var _string2 = _interopRequireDefault(_string); var _backing = require("backing"); var _backing2 = _interopRequireDefault(_backing); var _ = require("../.."); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const STRING_LENGTH_OFFSET = exports.STRING_LENGTH_OFFSET = 0; const STRING_HASH_OFFSET = exports.STRING_HASH_OFFSET = 4; const STRING_HEADER_SIZE = exports.STRING_HEADER_SIZE = 8; const STRING_DATA_OFFSET = exports.STRING_DATA_OFFSET = STRING_HEADER_SIZE; /** * Make a simple string type for the given realm. */ function make(realm) { const StringType = realm.StringType; const RawString = new StringType({ name: 'String', byteLength: 8, // Pointer byteAlignment: 8, constructor: function constructor(input) { if (this instanceof RawString) { throw new TypeError(`String is not a constructor.`); } else { return input == null ? '' : '' + input; } }, cast: function cast(input) { return input == null ? '' : '' + input; }, accepts: function accepts(input) { return typeof input === 'string'; }, initialize: function initialize(backing, pointerAddress, initialInput) { if (!initialInput) { backing.setFloat64(pointerAddress, 0); } else { backing.setFloat64(pointerAddress, createRawString(backing, initialInput)); } }, store: function store(backing, pointerAddress, input) { const existing = backing.getFloat64(pointerAddress); if (!input) { if (existing !== 0) { backing.gc.unref(existing); backing.setFloat64(pointerAddress, 0); } } else { const address = createRawString(backing, input); if (address !== existing) { if (existing !== 0) { backing.gc.unref(existing); } backing.setFloat64(pointerAddress, address); } } }, load: function load(backing, address) { return getString(backing, backing.getFloat64(address)); }, clear: function clear(backing, address) { const existing = backing.getFloat64(address); if (existing !== 0) { backing.gc.unref(existing); backing.setFloat64(address, 0); } }, randomValue: _string2.default, emptyValue: function emptyValue() { return ''; }, hashValue: hashString, equal: function equal(valueA, valueB) { return valueA === valueB; }, flowType: function flowType() { return `string`; } }); return RawString; } /** * Store the given raw string and return the address. * The string will NOT be interned. */ function createRawString(backing, input) { let hash = 0x811c9dc5; let allAscii = true; const length = input.length; for (let i = 0; i < length; i++) { const code = input.charCodeAt(i); if (code > 127) { allAscii = false; } hash ^= code; hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); } hash = hash >>> 0; return storeString(backing, input, hash, allAscii); } (0, _performance.forceInline)(createRawString); /** * Store a string and return its address. */ function storeString(backing, input, hash, allAscii) { if (allAscii) { return storeAsciiString(backing, input, hash); } else { return storeMultibyteString(backing, input, hash); } } (0, _performance.forceInline)(storeString); function storeAsciiString(backing, input, hash) { const length = input.length; const byteLength = length + STRING_HEADER_SIZE; const address = backing.gc.alloc(byteLength, 0, 1); backing.setInt32(address, length); backing.setUint32(address + STRING_HASH_OFFSET, hash); const offset = backing.offsetFor(address + STRING_DATA_OFFSET); const chars = backing.arenaFor(address).uint8Array; for (let i = 0; i < length; i++) { chars[offset + i] = input.charCodeAt(i); } return address; } (0, _performance.forceInline)(storeAsciiString); function storeMultibyteString(backing, input, hash) { const length = input.length; const byteLength = length + length + STRING_HEADER_SIZE; const address = backing.gc.alloc(byteLength, 0, 1); backing.setInt32(address, -length); backing.setUint32(address + STRING_HASH_OFFSET, hash); const offset = backing.offsetFor(address + STRING_DATA_OFFSET) >> 1; const chars = backing.arenaFor(address).uint16Array; for (let i = 0; i < length; i++) { chars[offset + i] = input.charCodeAt(i); } return address; } (0, _performance.forceInline)(storeMultibyteString); /** * Returns the hash for the given string. */ function hashString(input) { let hash = 0x811c9dc5; for (let i = 0; i < input.length; i++) { hash ^= input.charCodeAt(i); hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); } return hash >>> 0; } (0, _performance.forceInline)(hashString); /** * Check that the string stored at the given address matches the given input + hash. */ function checkEqual(backing, address, input, hash, allAscii) { if (getStringHash(backing, address) !== hash) { return false; } let length = backing.getInt32(address); if (length < 0) { if (allAscii) { return false; } length = -length; if (length !== input.length) { return false; } const arena = backing.arenaFor(address); const chars = arena.uint16Array; const offset = backing.offsetFor(address + STRING_HEADER_SIZE) >> 1; for (let i = 0; i < length; i++) { if (input.charCodeAt(i) !== chars[offset + i]) { return false; } } return true; } else { if (!allAscii) { return false; } else if (length !== input.length) { return false; } const arena = backing.arenaFor(address); const chars = arena.uint8Array; const offset = backing.offsetFor(address + STRING_HEADER_SIZE); for (let i = 0; i < length; i++) { if (input.charCodeAt(i) !== chars[offset + i]) { return false; } } return true; } } (0, _performance.forceInline)(checkEqual); /** * Read the string at the given address. */ function getString(backing, address) { if (address === 0) { return ''; } const arena = backing.arenaFor(address); let offset = backing.offsetFor(address); const length = arena.int32Array[offset >> 2]; if (length < 0) { offset = offset + STRING_DATA_OFFSET >> 1; return String.fromCharCode(...arena.uint16Array.slice(offset, offset + Math.abs(length))); } else { offset = offset + STRING_DATA_OFFSET; return String.fromCharCode(...arena.uint8Array.slice(offset, offset + Math.abs(length))); } } (0, _performance.forceInline)(getString); /** * Read the hash for the given string. */ function getStringHash(backing, address) { return backing.getUint32(address + STRING_HASH_OFFSET); } (0, _performance.forceInline)(getStringHash);