UNPKG

reign

Version:

A persistent, typed-objects implementation.

704 lines (622 loc) 23.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseArray = exports.MIN_TYPE_ID = undefined; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); exports.make = make; var _backing = require("backing"); var _backing2 = _interopRequireDefault(_backing); var _ = require("../"); var _util = require("../../util"); var _2 = require("../../"); var _symbols = require("../../symbols"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var MIN_TYPE_ID = exports.MIN_TYPE_ID = Math.pow(2, 20) * 4; var BaseArray = exports.BaseArray = function (_TypedObject) { _inherits(BaseArray, _TypedObject); function BaseArray() { _classCallCheck(this, BaseArray); return _possibleConstructorReturn(this, Object.getPrototypeOf(BaseArray).apply(this, arguments)); } _createClass(BaseArray, [{ key: "forEach", /** * Visit every item in the typed array. */ value: function forEach(visitor) { // Issue 252 var ElementType = this[_symbols.$ElementType]; // Issue 252 var backing = this[_symbols.$Backing]; // Issue 252 var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); var current = backing.getFloat64(address); for (var i = 0; i < length; i++) { visitor(ElementType.load(backing, current), i, this); current += this.BYTES_PER_ELEMENT; } return this; } /** * Map over every item in the typed array and return a new `Array` containing the result. */ }, { key: "map", value: function map(visitor) { // Issue 252 var ElementType = this[_symbols.$ElementType]; // Issue 252 var backing = this[_symbols.$Backing]; // Issue 252 var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); var array = new Array(length); var current = backing.getFloat64(address); for (var i = 0; i < length; i++) { array[i] = visitor(ElementType.load(backing, current), i, this); current += this.BYTES_PER_ELEMENT; } return array; } /** * Filter the items in the array and return a new array containing the results. */ }, { key: "filter", value: function filter(filterer) { // Issue 252 var ElementType = this[_symbols.$ElementType]; // Issue 252 var backing = this[_symbols.$Backing]; // Issue 252 var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); var array = []; var current = backing.getFloat64(address); for (var i = 0; i < length; i++) { var item = ElementType.load(backing, current); if (filterer(item, i, this)) { array.push(item); } current += this.BYTES_PER_ELEMENT; } return array; } /** * Applies a function against an accumulator and each value of the array * (from left-to-right) to reduce it to a single value. */ }, { key: "reduce", value: function reduce(reducer, initialValue) { // Issue 252 var ElementType = this[_symbols.$ElementType]; // Issue 252 var backing = this[_symbols.$Backing]; // Issue 252 var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); if (length === 0) { return initialValue; } var result = initialValue; var current = backing.getFloat64(address); var index = 0; if (initialValue === undefined) { initialValue = ElementType.load(backing, current); current += this.BYTES_PER_ELEMENT; index = 1; } for (; index < length; index++) { result = reducer(initialValue, ElementType.load(backing, current), index, this); current += this.BYTES_PER_ELEMENT; } return result; } /** * Return a representation of the array which can be encoded as JSON. */ }, { key: "toJSON", value: function toJSON() { // Issue 252 var ElementType = this[_symbols.$ElementType]; // Issue 252 var backing = this[_symbols.$Backing]; // Issue 252 var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); var array = new Array(length); var current = backing.getFloat64(address); for (var i = 0; i < length; i++) { array[i] = ElementType.load(backing, current); current += this.BYTES_PER_ELEMENT; } return array; } /** * Typed array iterator. * Issue 252 */ }, { key: Symbol.iterator, value: regeneratorRuntime.mark(function value() { var ElementType, backing, address, pointer, length, i; return regeneratorRuntime.wrap(function value$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: ElementType = this[_symbols.$ElementType]; backing = this[_symbols.$Backing]; address = this[_symbols.$Address]; pointer = backing.getFloat64(address); length = backing.getUint32(address + 8); i = 0; case 6: if (!(i < length)) { _context.next = 12; break; } _context.next = 9; return ElementType.load(backing, pointer + i * this.BYTES_PER_ELEMENT); case 9: i++; _context.next = 6; break; case 12: case "end": return _context.stop(); } } }, value, this); }) }, { key: "length", /** * Return the length of the array. */ get: function get() { // Issue 252 return this[_symbols.$Backing].getUint32(this[_symbols.$Address] + 8); } }]); return BaseArray; }(_.TypedObject); /** * The number of slots which have so far been defined. */ var definedSlotCount = 0; /** * Ensure that the typed array prototype has at least the given number of slots. */ function ensureSlots(min) { if (definedSlotCount >= min) { return; } var max = Math.max(min, definedSlotCount * 1.5) + 100; var _loop = function _loop(_index) { Object.defineProperty(BaseArray.prototype, _index, { get: function get() { return this[_symbols.$GetElement](_index); }, set: function set(value) { return this[_symbols.$SetElement](_index, value); } }); definedSlotCount++; }; for (var _index = definedSlotCount; _index < max; _index++) { _loop(_index); } } /** * Makes a TypedArray type class for a given realm. */ function make(realm) { var TypeClass = realm.TypeClass; var ReferenceType = realm.ReferenceType; var backing = realm.backing; var typeCounter = 0; return new TypeClass('ArrayType', function (ElementType) { var config = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; return function (Partial) { // Issue 252 var mustClearElements = ElementType[_symbols.$CanBeEmbedded] && ElementType[_symbols.$CanContainReferences]; // Issue 252 Partial[_symbols.$CanBeEmbedded] = false; // Issue 252 Partial[_symbols.$CanBeReferenced] = true; // Issue 252 Partial[_symbols.$CanContainReferences] = ElementType[_symbols.$CanContainReferences]; var MultidimensionalArray = void 0; var name = typeof config.name === 'string' ? config.name : typeof ElementType.name === 'string' && ElementType.name.length ? "Array<" + ElementType.name + ">" : "%Array<0x" + typeCounter.toString(16) + ">"; if (realm.T[name]) { return realm.T[name]; } typeCounter++; var id = typeof config.id === 'number' && config.id > 0 ? config.id : MIN_TYPE_ID + typeCounter; // Issue 285 Object.defineProperties(Partial, { name: { value: name }, Array: { get: function get() { if (MultidimensionalArray === undefined) { MultidimensionalArray = new realm.ArrayType(Partial); } return MultidimensionalArray; } } }); Partial.ref = new ReferenceType(Partial); var prototype = Object.create(BaseArray.prototype); prototype[_symbols.$ElementType] = ElementType; var BYTES_PER_ELEMENT = (0, _util.alignTo)(ElementType.byteLength, ElementType.byteAlignment); prototype.BYTES_PER_ELEMENT = BYTES_PER_ELEMENT; Partial.BYTES_PER_ELEMENT = BYTES_PER_ELEMENT; /** * The constructor for array type instances. */ function constructor(backingOrInput, address) { if (backingOrInput instanceof _backing2.default) { this[_symbols.$Backing] = backingOrInput; this[_symbols.$Address] = address; ensureSlots(this.length); } else { this[_symbols.$Backing] = backing; this[_symbols.$Address] = createArray(backing, backingOrInput); } } /** * Get an element at the given index. */ prototype[_symbols.$GetElement] = function GetElement(index) { var normalizedIndex = index >>> 0; var backing = this[_symbols.$Backing]; var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); if (length === 0 || normalizedIndex >= length) { throw new RangeError("Cannot get an element at index " + normalizedIndex + " from an array of length " + length + "."); } var pointer = backing.getFloat64(address); return ElementType.load(backing, pointer + normalizedIndex * BYTES_PER_ELEMENT); }; /** * Set an element at the given index. */ prototype[_symbols.$SetElement] = function SetElement(index, value) { var normalizedIndex = index >>> 0; var backing = this[_symbols.$Backing]; var address = this[_symbols.$Address]; var length = backing.getUint32(address + 8); if (length === 0 || normalizedIndex >= length) { throw new RangeError("Cannot set an element at index " + normalizedIndex + " in an array of length " + length + "."); } var pointer = backing.getFloat64(address); return ElementType.store(backing, pointer + normalizedIndex * BYTES_PER_ELEMENT, value); }; /** * Allocate space for the given array and write the input if any. */ function createArray(backing, input) { if (input == null) { var address = backing.gc.alloc(16); backing.setFloat64(address, 0); backing.setUint32(address + 8, 0); return address; } else if (typeof input === 'number') { if (input >>> 0 !== input) { throw new TypeError("Cannot create a typed array with an invalid length."); } else if (input === 0) { var _address = backing.gc.alloc(16); backing.setFloat64(_address, 0); backing.setUint32(_address + 8, 0); return _address; } else { ensureSlots(input); var byteLength = input * BYTES_PER_ELEMENT; var _address2 = backing.gc.alloc(byteLength + 16); backing.setFloat64(_address2, _address2 + 16); backing.setUint32(_address2 + 8, input); writeDefaultValues(backing, _address2 + 16, input); return _address2; } } else if ((typeof input === "undefined" ? "undefined" : _typeof(input)) === 'object') { var array = void 0; if (Array.isArray(input)) { array = input; } else if (input[Symbol.iterator]) { array = Array.from(input); } else { throw new TypeError("Cannot create a typed array from a non-iterable input."); } if (array.length === 0) { var _address3 = backing.gc.alloc(16); backing.setFloat64(_address3, 0); backing.setUint32(_address3 + 8, 0); return _address3; } else { ensureSlots(array.length); var _byteLength = array.length * BYTES_PER_ELEMENT; var _address4 = backing.gc.alloc(_byteLength + 16); backing.setFloat64(_address4, _address4 + 16); backing.setUint32(_address4 + 8, array.length); writeValues(backing, _address4 + 16, array); return _address4; } } else { throw new TypeError("Cannot create a typed array from invalid input."); } } /** * Write empty values to the given address. */ function writeDefaultValues(backing, address, length) { var current = address; for (var i = 0; i < length; i++) { ElementType.initialize(backing, current); current += BYTES_PER_ELEMENT; } return address; } /** * Write values to the given address. */ function writeValues(backing, address, input) { var length = input.length; var current = address; for (var i = 0; i < length; i++) { ElementType.initialize(backing, current, input[i]); current += BYTES_PER_ELEMENT; } return address; } /** * Initialize the given array at the given address. */ function initializeArray(backing, address, input) { if (input == null) { backing.setFloat64(address, 0); backing.setUint32(address + 8, 0); } else if ((typeof input === "undefined" ? "undefined" : _typeof(input)) === 'object') { var array = void 0; if (Array.isArray(input)) { array = input; } else if (input[Symbol.iterator]) { array = Array.from(input); } else { throw new TypeError("Cannot create a typed array from a non-iterable input."); } if (array.length === 0) { backing.setFloat64(address, 0); backing.setUint32(address + 8, 0); } else { var byteLength = array.length * BYTES_PER_ELEMENT; var dataAddress = backing.alloc(byteLength); backing.setFloat64(address, dataAddress); backing.setUint32(address + 8, array.length); writeValues(backing, dataAddress, array); } } else { throw new TypeError("Cannot create a typed array from invalid input."); } } /** * Store the given array at the given address. */ function storeArray(backing, address, input) { var existing = backing.getFloat64(address); if (existing > 0) { if (mustClearElements) { var length = backing.getUint32(address + 8); var current = existing; for (var i = 0; i < length; i++) { ElementType.clear(backing, current); current += BYTES_PER_ELEMENT; } } backing.free(existing); } if (input == null) { backing.setFloat64(address, 0); backing.setUint32(address + 8, 0); } else if ((typeof input === "undefined" ? "undefined" : _typeof(input)) === 'object') { var array = void 0; if (Array.isArray(input)) { array = input; } else if (input[Symbol.iterator]) { array = Array.from(input); } else { throw new TypeError("Cannot create a typed array from a non-iterable input."); } if (array.length === 0) { backing.setFloat64(address, 0); backing.setUint32(address + 8, 0); } else { var byteLength = array.length * BYTES_PER_ELEMENT; var dataAddress = backing.alloc(byteLength); backing.setFloat64(address, dataAddress); backing.setUint32(address + 8, array.length); writeValues(backing, dataAddress, array); } } else { throw new TypeError("Cannot create a typed array from invalid input."); } } /** * Load the array at the given address. */ function loadArray(backing, address) { return new Partial(backing, address); } /** * Hash the given array. */ function hashArray(array) { var backing = array[_symbols.$Backing]; var address = array[_symbols.$Address]; var current = backing.getFloat64(address); var length = backing.getUint32(address + 8); var hash = 0x811c9dc5; for (var i = 0; i < length; i++) { hash ^= ElementType.hashValue(ElementType.load(backing, current)); hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); current += BYTES_PER_ELEMENT; } return hash >>> 0; } /** * Return a random array. */ function randomArray() { var length = Math.floor(Math.random() * Math.pow(2, 7)); var array = new Array(length); for (var i = 0; i < length; i++) { array[i] = ElementType.randomValue(); } return new Partial(array); } return { id: id, name: name, byteAlignment: 8, byteLength: 16, gc: true, constructor: constructor, prototype: prototype, accepts: function accepts(input) { return input == null || input instanceof Partial || Array.isArray(input) || input[Symbol.iterator]; }, cast: function cast(input) { if (input instanceof Partial) { return input; } else { return new Partial(input); } }, initialize: initializeArray, store: storeArray, load: loadArray, clear: function clear(backing, address) { var pointer = backing.getFloat64(address); var length = backing.getUint32(address + 8); var current = pointer; for (var i = 0; i < length; i++) { ElementType.clear(backing, current); current += BYTES_PER_ELEMENT; } }, destructor: function destructor(backing, address) { var pointer = backing.getFloat64(address); if (mustClearElements) { var length = backing.getUint32(address + 8); var current = pointer; for (var i = 0; i < length; i++) { ElementType.clear(backing, current); current += BYTES_PER_ELEMENT; } } if (pointer !== address + 16) { // this was allocated using `BaseArray.store()` // so we need to reclaim the data segment separately backing.free(pointer); } backing.setFloat64(address, 0); backing.setUint32(address + 8, 0); }, equal: function equal(arrayA, arrayB) { if (arrayA[_symbols.$Backing] === arrayB[_symbols.$Backing] && arrayA[_symbols.$Address] === arrayB[_symbols.$Address]) { return true; } else if (arrayA.length !== arrayB.length) { return false; } var length = arrayA.length; for (var i = 0; i < length; i++) { if (!ElementType.equal(arrayA[i], arrayB[i])) { return false; } } return true; }, compareValues: function compareValues(valueA, valueB) { if (valueA === valueB) { return 0; } else if (valueA.length > valueB.length) { return 1; } else if (valueA.length < valueB.length) { return -1; } var length = valueA.length; for (var i = 0; i < length; i++) { var result = ElementType.compareValues(valueA[i], valueB[i]); if (result !== 0) { return result; } } return 0; }, compareAddresses: function compareAddresses(backing, addressA, addressB) { if (addressA === addressB) { return 0; } else if (addressA === 0) { return -1; } else if (addressB === 0) { return 1; } var lengthA = backing.getUint32(addressA + 8); var lengthB = backing.getUint32(addressB + 8); if (lengthA > lengthB) { return 1; } else if (lengthB < lengthA) { return -1; } var locA = backing.getFloat64(addressA); var locB = backing.getFloat64(addressB); for (var i = 0; i < lengthA; i++) { var result = ElementType.compareAddresses(backing, locA, locB); if (result !== 0) { return result; } locA += BYTES_PER_ELEMENT; locB += BYTES_PER_ELEMENT; } return 0; }, compareAddressValue: function compareAddressValue(backing, address, value) { var length = backing.getUint32(address + 8); if (length === 0 && value.length === 0) { return 0; } else if (length > value.length) { return 1; } else if (length < value.length) { return -1; } var loc = backing.getFloat64(address); for (var i = 0; i < length; i++) { var result = ElementType.compareAddressValue(backing, loc, value[i]); if (result !== 0) { return result; } loc += BYTES_PER_ELEMENT; } return 0; }, emptyValue: function emptyValue() { return []; }, hashValue: hashArray, randomValue: randomArray, flowType: function flowType() { return "Array<" + ElementType.flowType() + ">"; } }; }; }); };