Source: kupyna/longlong_buffer.js

/*jslint node: true, esversion:6 */
'use strict';


/**
 * LongLongBuffer implements fixed size buffer (size defined at construction time), 
 * which allows to access data in form of both byte and 64-bit long values simultanously.
 * Any changes done by byte oriented access methods are reflected in 64 bit long representation.
 * 
 * If you want to read/set long data directly - use longArr property. 
 * Each 64 bit value is divided into 32 MSB longArr.hi subarray and 32 LSB longArr.lo subarrays.
 * This is done to the fact, that precise representation of integer value in javascript is limited to number up to 2^51.
 * If this value is exceeded, integer is switched to non precise double representation.
 * 
 * In case of having access to data casted to bytes, please use object methods.
 *  
 * @private
 */
class LongLongBuffer {

  /**
   * Constructs LongArrBuffer object
   * @param {Number} sizeInLongs - size of long array buffer - number of long elements in the array
   * @public
   */
  constructor(sizeInLongs) {
    this.longArr = {
      lo: [],
      hi: []
    };
    for (var index = 0; index < sizeInLongs; index++) {
      this.longArr.lo[index] = 0;
      this.longArr.hi[index] = 0;
    }
    this._byteArr = [];
    this.sizeInLongs = sizeInLongs;
    this.longArrUpdated = true;
    this._longArr2ByteArr();
  }

  /**
   * Converts byte array representation into internal 64bit long array representation.
   * @private
   */
  _byteArr2LongArr() {
    var start = 0;
    for (var longIndex = 0; longIndex < this.longArr.lo.length; longIndex++) {
      this.longArr.lo[longIndex] = (this._byteArr[start + 3] << 24) | (this._byteArr[start + 2] << 16) | (this._byteArr[start + 1] << 8) | this._byteArr[start];
      this.longArr.hi[longIndex] = (this._byteArr[start + 7] << 24) | (this._byteArr[start + 6] << 16) | (this._byteArr[start + 5] << 8) | this._byteArr[start + 4];
      start += 8;
    }
  }

  /**
   * Converts internal long array representation into byte array representation.
   * @private
   */
  _longArr2ByteArr() {
    if (this.longArr.lo.length > this.sizeInLongs ||
      this.longArr.hi.length > this.sizeInLongs) throw Error("modified initial length of long long array");

    if (!this.longArrUpdated) {
      return;
    }
    var start = 0;
    for (var longIndex = 0; longIndex < this.longArr.lo.length; longIndex++) {
      var tempLo = this.longArr.lo[longIndex];
      var tempHi = this.longArr.hi[longIndex];
      for (var byteIndex = 0; byteIndex < 4; byteIndex++) {
        this._byteArr[start] = tempLo & 0xff;
        this._byteArr[start + 4] = tempHi & 0xff;
        start++;
        tempLo >>= 8;
        tempHi >>= 8;
      }
      start += 4;
    }
    this.longArrUpdated = false;
  }

  /**
   * Converts single long value into array of bytes.
   * @param {Number} longValue - long number to be converted into byte array
   * @returns {Array} - converted number
   * @private
   */
  _long2ByteArr(longValue) {
    var tempArr = [];
    tempArr[0] = longValue & 0xff;
    tempArr[1] = (longValue >> 8) & 0xff;
    tempArr[2] = (longValue >> 16) & 0xff;
    tempArr[3] = (longValue >> 24) & 0xff;
    return tempArr;
  }

  /**
   * Notifies, that long array content has been updated.
   * If you changed 64 bit long data accessing longArr object property,
   * this methods needs to be called to ensure internal integrity between
   * byte and 64-bit long representation. 
   * @public
   */
  notifyLongUpdated() {
    this.longArrUpdated = true;
  }

  /**
   * Copies bytes array into long array buffer.
   * @param {Array} src - source array to copy from
   * @param {Integer} srcPos - souce location to start copy from (in bytes)
   * @param {Integer} dstPos - destination location (in bytes) of buffer to copy data to
   * @param {Integer} len - length of data to be copied (in bytes)
   * @public
   */
  copyBytesTo(src, srcPos, dstPos, len) {
    this._longArr2ByteArr();
    for (var index = 0; index < len; index++) {
      var srcByte = src[srcPos + index];
      if (srcByte < 0 || srcByte > 255) throw new Error("array should contain only bytes - pos:" + index + " value:" + srcByte);
      this._byteArr[dstPos + index] = src[srcPos + index];
    }
    this._byteArr2LongArr();
  }

  /**
   * Copies bytes subarray from the long array buffer
   * @param {Integer} srcPos - location of long array buffer to copy from (indexed in bytes)
   * @param {Array} dst - destination array to copy data to
   * @param {Integer} dstPos - start index of destination array
   * @param {Integer} len - length of data to be copied (in bytes)
   * @public
   */
  copyBytesFrom(srcPos, dst, dstPos, len) {
    this._longArr2ByteArr();
    for (var index = 0; index < len; index++) {
      dst[index + dstPos] = this._byteArr[index + srcPos];
    }
  }

  /**
   * Sets value of particular byte in the buffer
   * @param {Integer} bytePos - location of buffer to be change (indexed in bytes)
   * @param {Integer} byteValue - value to be set
   * @public
   */
  setByte(bytePos, byteValue) {
    this._longArr2ByteArr();
    if (byteValue < 0 || byteValue > 255) throw new Error("byteValue is not byte");

    this._byteArr[bytePos] = byteValue;
    this._byteArr2LongArr();
  }

  /**
   * Zeroes buffer.
   * @public
   */
  zeroAll() {
    for (var index = 0; index < this.longArr.lo.length; index++) {
      this.longArr.lo[index] = this.longArr.hi[index] = 0;
    }
    this.notifyLongUpdated();
    this._longArr2ByteArr();
  }

  /**
   * Zeroes range of bytes inside buffer.
   * @param {Integer} startPos - location of first byte to be zeroed
   * @param {Integer} len - length of bytes to be zeroed
   * @public
   */
  zeroBytes(startPos, len) {
    this._longArr2ByteArr();
    for (var index = 0; index < len; index++) {
      this._byteArr[index + startPos] = 0;
    }
    this._byteArr2LongArr();
  }

  /**
   * Sets long (32 bit) value inside buffer. Four bytes starting from pos location will be set.
   * @param {Number} pos - start location of 32-bit long value to be changed (indexed in bytes)
   * @param {Number} longValue - 32-bit long value to be set
   */
  setLongAsBytes(pos, longValue) {
    if (longValue < 0 || longValue > 0xffff)
      throw new Error("invalid 32 bit long value:" + longValue);
    var tempArr = this._long2ByteArr(longValue);
    this.copyBytesTo(tempArr, 0, pos, tempArr.length);
  }
}

module.exports = LongLongBuffer;