From 95cd93ad4d78f81caf1c3142a836737dbc2f431c Mon Sep 17 00:00:00 2001 From: ferricles Date: Fri, 24 Jun 2022 00:15:06 -0700 Subject: [PATCH] major bug fixes, append number yet to be unit tested --- js/utils.js | 150 ++++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 74 deletions(-) diff --git a/js/utils.js b/js/utils.js index 5a56be2..dd68037 100644 --- a/js/utils.js +++ b/js/utils.js @@ -159,46 +159,40 @@ Base64 = (function () { /** Constructs an arbitrary-length bit vector. * @class - * @param {String | Number} data - * @param {Number} length - a set length for the data. Must be in the range [0, 32] if data is a Number. + * @param {String | Number} data - The data to append. + * @param {Number} length - A set length for the data. Ignored if data is a string. * * The structure of the Uint32Array should be [[last, ..., first], ..., [last, ..., first], [empty space, last, ..., first]] */ constructor(data, length) { let bit_vec = []; - if (length < 0) { - throw new RangeError("BitVector must have nonnegative length."); - } if (typeof data === "string") { - //string in B64 - let curr = 0; - let total_bits = 0; - let i = 0; + let int = 0; + let bv_idx = 0; + length = data.length * 6; - //override length passed in if it's > length of string naturally to save space - length = Math.min(length, data.length * 6); - - while (i < data.length && total_bits < length) { - let int = Base64.toInt(data[i]) - //total_bits implicitly % 32 here - curr |= (int << total_bits); - - if (total_bits % 32 > 25) { - //push and roll over uncaught bits - bit_vec.push(curr); - curr = (int >>> (32 - (total_bits % 32))); + for (let i = 0; i < data.length; i++) { + let char = Base64.toInt(data[i]); + let pre_pos = bv_idx % 32; + int |= (char << bv_idx); + bv_idx += 6; + let post_pos = bv_idx % 32; + if (post_pos < pre_pos) { //we have to have filled up the integer + bit_vec.push(int); + int = (char >>> (6 - post_pos)); } - i++; - total_bits += 6; - } - - //need to push remaining bits if not pushed yet - if (total_bits % 32 != 0) { - bit_vec.push(curr); + if (i == data.length - 1 && post_pos != 0) { + bit_vec.push(int); + } } } else if (typeof data === "number") { + if (typeof length === "undefined") + if (length < 0) { + throw new RangeError("BitVector must have nonnegative length."); + } + //convert to int just in case data = Math.round(data); @@ -217,12 +211,12 @@ Base64 = (function () { /** Return value of bit at index idx. * - * @param {Number} idx - the index to read + * @param {Number} idx - The index to read * - * @returns the bit value at position idx + * @returns The bit value at position idx */ read_bit(idx) { - if (idx < 0 || idx > this.length) { + if (idx < 0 || idx >= this.length) { throw new RangeError("Cannot read bit outside the range of the BitVector."); } return ((this.bits[Math.floor(idx / 32)] & (1 << (idx % 32))) == 0 ? 0 : 1); @@ -230,10 +224,10 @@ Base64 = (function () { /** Returns an integer value (if possible) made from the range of bits [start, end). Undefined behavior if the range to read is too big. * - * @param {Number} start - the index to start slicing from. Inclusive - * @param {Number} end - the index to end slicing at. Exclusive + * @param {Number} start - The index to start slicing from. Inclusive. + * @param {Number} end - The index to end slicing at. Exclusive. * - * @returns an integer representation of the sliced bits. + * @returns An integer representation of the sliced bits. */ slice(start, end) { //TO NOTE: JS shifting is ALWAYS in mod 32. a << b will do a << (b mod 32) implicitly. @@ -269,7 +263,7 @@ Base64 = (function () { /** Assign bit at index idx to 1. * - * @param {Number} idx - the index to set + * @param {Number} idx - The index to set. */ set_bit(idx) { if (idx < 0 || idx > this.length) { @@ -280,7 +274,7 @@ Base64 = (function () { /** Assign bit at index idx to 0. * - * @param {Number} idx - the index to clear + * @param {Number} idx - The index to clear. */ clear_bit(idx) { if (idx < 0 || idx > this.length) { @@ -291,7 +285,7 @@ Base64 = (function () { /** Creates a string version of the bit vector in B64. Does not keep the order of elements a sensible human readable format. * - * @returns a b64 string representation of the BitVector + * @returns A b64 string representation of the BitVector. */ toB64() { if (this.length == 0) { @@ -309,7 +303,7 @@ Base64 = (function () { /** Returns a BitVector in bitstring format. Probably only useful for dev debugging. * - * @returns a bit string representation of the BitVector. Goes from higher-indexed bits to lower-indexed bits. + * @returns A bit string representation of the BitVector. Goes from higher-indexed bits to lower-indexed bits. (n ... 0) */ toString() { let ret_str = ""; @@ -319,10 +313,22 @@ Base64 = (function () { return ret_str; } + /** Returns a BitVector in bitstring format. Probably only useful for dev debugging. + * + * @returns A bit string representation of the BitVector. Goes from lower-indexed bits to higher-indexed bits. (0 ... n) + */ + toStringR() { + let ret_str = ""; + for (let i = 0; i < this.length; i++) { + ret_str += (this.read_bit(i) == 0 ? "0": "1"); + } + return ret_str; + } + /** Appends data to the BitVector. * - * @param {Number | String} data - * @param {Number} length - the length, in bits, of the new data + * @param {Number | String} data - The data to append. + * @param {Number} length - The length, in bits, of the new data. This is ignored if data is a string. */ append(data, length) { if (length < 0) { @@ -334,56 +340,52 @@ Base64 = (function () { bit_vec.push(uint); } if (typeof data === "string") { - //string in B64 - let curr = bit_vec[Math.floor(this.length / 32)]; - let total_bits = this.length; - let i = 0; - - //override length passed in if it's > length of string naturally to save space - length = Math.min(length, data.length * 6); - - while (i < data.length && total_bits < this.length + length) { - let int = Base64.toInt(data[i]) - //total_bits implicitly % 32 here - curr |= (int << total_bits); - - if (total_bits % 32 > 25) { - //push and roll over uncaught bits - if (bit_vec.length == (Math.floor(this.length / 32) + 1)) { - bit_vec[Math.floor(this.length / 32)] = curr; + let int = bit_vec[bit_vec.length - 1]; + let bv_idx = this.length; + length = data.length * 6; + let updated_curr = false; + for (let i = 0; i < data.length; i++) { + let char = Base64.toInt(data[i]); + let pre_pos = bv_idx % 32; + int |= (char << bv_idx); + bv_idx += 6; + let post_pos = bv_idx % 32; + if (post_pos < pre_pos) { //we have to have filled up the integer + if (bit_vec.length == this.bits.length && !updated_curr) { + bit_vec[bit_vec.length - 1] = int; + updated_curr = true; } else { - bit_vec.push(curr); + bit_vec.push(int); } - curr = (int >>> (32 - (total_bits % 32))); + int = (char >>> (6 - post_pos)); } - i++; - total_bits += 6; - } - - //need to push remaining bits if not pushed yet - if (total_bits % 32 != 0) { - bit_vec.push(curr); + if (i == data.length - 1) { + if (bit_vec.length == this.bits.length && !updated_curr) { + bit_vec[bit_vec.length - 1] = int; + } else if (post_pos != 0) { + bit_vec.push(int); + } + } } } else if (typeof data === "number") { //convert to int just in case let int = Math.round(data); - //range of numbers that "could" fit in a uint32 -> [0, 2^32) U (-2^31, 2^31) - if (data > 2**32 - 1 || data < -(2 ** 31 - 1)) { + //range of numbers that "could" fit in a uint32 -> [0, 2^32) U [-2^31, 2^31) + if (data > 2**32 - 1 || data < -(2 ** 31)) { throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector."); } - //could be split between multiple new ints //reminder that shifts implicitly mod 32 - bit_vec[Math.floor(this.length / 32)] |= ((int & ~((~0) << length)) << (this.length)); - if (Math.floor((this.length + length) / 32) > Math.floor(this.length / 32)) { - bit_vec.push(int >>> (this.length)); + bit_vec[bit_vec.length - 1] |= ((int & ~((~0) << length)) << (this.length)); + if (((this.length + length) % 32 < ((this.length - 1) % 32) + 1) || ((this.length + length) % 32 != 0)) { + bit_vec.push(int >>> (32 - this.length)); } } else { throw new TypeError("BitVector must be appended with a Number or a B64 String"); } - + this.bits = new Uint32Array(bit_vec); this.length += length; } @@ -650,4 +652,4 @@ async function hardReload() { function capitalizeFirst(str) { return str[0].toUpperCase() + str.substring(1); -} \ No newline at end of file +}