From 2d719e9a4e4a5d43e10699c91d951d1542b52d3a Mon Sep 17 00:00:00 2001 From: ferricles Date: Sun, 19 Jun 2022 12:05:01 -0700 Subject: [PATCH 01/10] BitVector class --- js/utils.js | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/js/utils.js b/js/utils.js index fed7df8..07e984b 100644 --- a/js/utils.js +++ b/js/utils.js @@ -98,6 +98,8 @@ function log(b, n) { // https://stackoverflow.com/a/27696695 // Modified for fixed precision +// Base64.fromInt(-2147483648); // gives "200000" +// Base64.toInt("200000"); // gives -2147483648 Base64 = (function () { var digitsStr = // 0 8 16 24 32 40 48 56 63 @@ -149,8 +151,38 @@ Base64 = (function () { }; })(); -// Base64.fromInt(-2147483648); // gives "200000" -// Base64.toInt("200000"); // gives -2147483648 + +/** A class used to represent an arbitrary length bit vector. Very useful for encoding and decoding. + * + */ + class BitVector { + + /** Constructs an arbitrary-length bit vector. + * @class + * @param {String | Number} + */ + constructor(data, length) { + + /** @private + * @type {UInt8Array} + */ + if (length) { + if (typeof data === "string") { + this.bits = Uint8Array(); + } else if (typeof data === "number") { + this.bits = Uint8Array(); + } + } else { + if (typeof data === "string") { + this.bits = Uint8Array(); + } else if (typeof data === "number") { + this.bits = Uint8Array(); + } + } + + + } +} /* Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative. From 28955fe66172a9d9fad38bbf4a90d34339b9c15a Mon Sep 17 00:00:00 2001 From: ferricles Date: Mon, 20 Jun 2022 12:07:25 -0700 Subject: [PATCH 02/10] temp --- js/utils.js | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/js/utils.js b/js/utils.js index 07e984b..885db24 100644 --- a/js/utils.js +++ b/js/utils.js @@ -163,27 +163,14 @@ Base64 = (function () { */ constructor(data, length) { - /** @private - * @type {UInt8Array} - */ - if (length) { - if (typeof data === "string") { - this.bits = Uint8Array(); - } else if (typeof data === "number") { - this.bits = Uint8Array(); - } - } else { - if (typeof data === "string") { - this.bits = Uint8Array(); - } else if (typeof data === "number") { - this.bits = Uint8Array(); - } - } + this.bitvector = NumberInt(); } } +let bitv = new BitVector(123123, 1); + /* Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative. Pct would be 0.80 for 80%, -1.20 for 120%, etc From 64e076cbb483e97d622f6bb11aa00c679553bd30 Mon Sep 17 00:00:00 2001 From: ferricles Date: Tue, 21 Jun 2022 21:51:26 -0700 Subject: [PATCH 03/10] dummy commit --- js/utils.js | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/js/utils.js b/js/utils.js index 885db24..d83681c 100644 --- a/js/utils.js +++ b/js/utils.js @@ -159,17 +159,96 @@ Base64 = (function () { /** Constructs an arbitrary-length bit vector. * @class - * @param {String | Number} + * @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. + * + * 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."); + } - this.bitvector = NumberInt(); + if (typeof data === "string") { + //string in B64 + let str_len = data.length; + let curr = 0; + let curr_bits = 0; + } else if (typeof data === "number") { + //convert to int just in case + data = Math.round(data); - + //range of numbers that won't fit in a uint32 + if (data > 2**32 - 1 || data < -(2 ** 32 - 1)) { + throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector."); + } + bit_vec.push(data); + } else if (data instanceof Array) [ + + ] + + this.length = length; + this.bits = new Uint32Array(bit_vec); } -} -let bitv = new BitVector(123123, 1); + + readBitsSigned() { + + } + + readBitsUnsigned() { + + } + + setBits() { + + } + + clearBits() { + + } + + append(data, length) { + if (length < 0) { + throw new RangeError("BitVector length must increase by a nonnegative number."); + } + + this.length += length; + } + + /** Creates a string version of the bit vector + * + * @returns A bit vector in string format + */ + toString() { + if (this.length == 0) { + return ""; + } + + let bitstr = ""; + //extract bits from first uint32 - may not be all 32 bits + let length_first = this.length % 32; + let curr = this.bits[0]; + for (let i = 0; i < length_first; ++i) { + bitstr = (curr % 2 == 0 ? '0' : '1') + bitstr; + curr >>= 1; + } + + //extract bits from rest of uint32s - always all 32 bits + for (let i = 1; i < this.bits.length; ++i) { + curr = this.bits[i]; + for (let j = 0; j < 32; ++j) { + bitstr = (curr % 2 == 0 ? '0' : '1') + bitstr; + curr >>= 1; + } + } + + //return the formed bitstring + return bitstr; + } +}; + /* Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative. From cac7e6b04b3c30b361cfb98d1ed8611fd0156df2 Mon Sep 17 00:00:00 2001 From: ferricles Date: Wed, 22 Jun 2022 17:45:13 -0700 Subject: [PATCH 04/10] functional set bit, clear bit, read bit, slice --- js/utils.js | 93 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/js/utils.js b/js/utils.js index d83681c..7c2657f 100644 --- a/js/utils.js +++ b/js/utils.js @@ -184,40 +184,88 @@ Base64 = (function () { throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector."); } bit_vec.push(data); - } else if (data instanceof Array) [ - - ] + } else if (data instanceof Array) { + + } this.length = length; this.bits = new Uint32Array(bit_vec); } - - readBitsSigned() { - + /** Return value of bit at index idx. + * + * @param {Number} idx - the index to read + */ + read_bit(idx) { + 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); } - readBitsUnsigned() { + /** 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 + */ + slice(start, end) { + if (end < start) { + throw new RangeError("Cannot slice a range where the end is before the start."); + } else if (end == start) { + return 0; + } + let res = 0; + if (Math.floor((end - 1) / 32) == Math.floor(start / 32)) { + //the range is within 1 uint32 section - do some relatively fast bit twiddling + res = (this.bits[Math.floor(start / 32)] & ~((((~0) << ((end - 1) % 32)) << 1) | ~((~0) << (start % 32)))) >>> (start % 32); + } else { + //range is not within 1 section of the array - do ugly + for (let i = start; i < end; i++) { + res |= (get_bit(i) << (i - start)); + } + } + + return res; } - setBits() { - + /** Assign bit at index idx to 1. + * + * @param {Number} idx - the index to set + */ + set_bit(idx) { + if (idx < 0 || idx > this.length) { + throw new RangeError("Cannot set bit outside the range of the BitVector."); + } + this.bits[Math.floor(idx / 32)] |= (1 << idx % 32); } - clearBits() { - + /** Assign bit at index idx to 0. + * + * @param {Number} idx - the index to clear + */ + clear_bit(idx) { + if (idx < 0 || idx > this.length) { + throw new RangeError("Cannot clear bit outside the range of the BitVector."); + } + this.bits[Math.floor(idx / 32)] &= ~(1 << idx % 32); } + /** Appends data to the BitVector. + * + * @param {Number | String | Array} data + * @param {Number} length - the length, in bits, of the new data + */ append(data, length) { if (length < 0) { throw new RangeError("BitVector length must increase by a nonnegative number."); } this.length += length; + //add in new data } - /** Creates a string version of the bit vector + /** Creates a string version of the bit vector in B64. Does not keep the order of elements a sensible human readable format. * * @returns A bit vector in string format */ @@ -226,26 +274,7 @@ Base64 = (function () { return ""; } - let bitstr = ""; - //extract bits from first uint32 - may not be all 32 bits - let length_first = this.length % 32; - let curr = this.bits[0]; - for (let i = 0; i < length_first; ++i) { - bitstr = (curr % 2 == 0 ? '0' : '1') + bitstr; - curr >>= 1; - } - - //extract bits from rest of uint32s - always all 32 bits - for (let i = 1; i < this.bits.length; ++i) { - curr = this.bits[i]; - for (let j = 0; j < 32; ++j) { - bitstr = (curr % 2 == 0 ? '0' : '1') + bitstr; - curr >>= 1; - } - } - - //return the formed bitstring - return bitstr; + } }; From ab4de136f2d518423304323e2e84be3937ba5fbd Mon Sep 17 00:00:00 2001 From: ferricles Date: Thu, 23 Jun 2022 09:44:03 -0700 Subject: [PATCH 05/10] optimized slice(), string construction, toB64 and toString --- js/utils.js | 146 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 124 insertions(+), 22 deletions(-) diff --git a/js/utils.js b/js/utils.js index 7c2657f..7be0df3 100644 --- a/js/utils.js +++ b/js/utils.js @@ -172,9 +172,32 @@ Base64 = (function () { if (typeof data === "string") { //string in B64 - let str_len = data.length; let curr = 0; - let curr_bits = 0; + let total_bits = 0; + let i = 0; + 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))); + } + + i++; + total_bits += 6; + } + + //need to push remaining bits if not pushed yet + if (total_bits % 32 <= 25) { + bit_vec.push(curr); + } + + //override length passed in if it's > length of string naturally to save space + length = Math.min(length, data.length * 6); + } else if (typeof data === "number") { //convert to int just in case data = Math.round(data); @@ -184,8 +207,8 @@ Base64 = (function () { throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector."); } bit_vec.push(data); - } else if (data instanceof Array) { - + } else { + throw new TypeError("BitVector must be instantiated with a Number or a B64 String"); } this.length = length; @@ -195,6 +218,8 @@ Base64 = (function () { /** Return value of bit at index idx. * * @param {Number} idx - the index to read + * + * @returns the bit value at position idx */ read_bit(idx) { if (idx < 0 || idx > this.length) { @@ -207,26 +232,39 @@ Base64 = (function () { * * @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. */ slice(start, end) { + //TO NOTE: JS shifting is ALWAYS in mod 32. a << b will do a << (b mod 32) implicitly. + if (end < start) { throw new RangeError("Cannot slice a range where the end is before the start."); } else if (end == start) { return 0; + } else if (end - start > 32) { + //requesting a slice of longer than 32 bits (safe integer "length") + throw new RangeError("Cannot slice a range of longer than 32 bits (unsafe to store in an integer)."); } let res = 0; if (Math.floor((end - 1) / 32) == Math.floor(start / 32)) { //the range is within 1 uint32 section - do some relatively fast bit twiddling - res = (this.bits[Math.floor(start / 32)] & ~((((~0) << ((end - 1) % 32)) << 1) | ~((~0) << (start % 32)))) >>> (start % 32); + res = (this.bits[Math.floor(start / 32)] & ~((((~0) << ((end - 1))) << 1) | ~((~0) << (start)))) >>> (start % 32); } else { - //range is not within 1 section of the array - do ugly - for (let i = start; i < end; i++) { - res |= (get_bit(i) << (i - start)); - } + //the number of bits in the uint32s + let start_pos = (start % 32); + let int_idx = Math.floor(start/32); + res = (this.bits[int_idx] & ((~0) << (start))) >>> (start_pos); + res |= (this.bits[int_idx + 1] & ~((~0) << (end))) << (32 - start_pos); } return res; + + // General code - slow + // for (let i = start; i < end; i++) { + // res |= (get_bit(i) << (i - start)); + // } } /** Assign bit at index idx to 1. @@ -251,30 +289,94 @@ Base64 = (function () { this.bits[Math.floor(idx / 32)] &= ~(1 << idx % 32); } + /** 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 + */ + toB64() { + if (this.length == 0) { + return ""; + } + let b64_str = ""; + let i = 0; + while (i < this.length) { + b64_str += Base64.fromIntV(this.slice(i, i + 6), 1); + i += 6; + } + + return b64_str; + } + + /** Returns a BitVector in bitstring format. Probably only useful for dev debugging. + * + * @returns a bit string representation of the BitVector + */ + toString() { + 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 | Array} data + * @param {Number | String} data * @param {Number} length - the length, in bits, of the new data */ - append(data, length) { + append(data, length) { if (length < 0) { throw new RangeError("BitVector length must increase by a nonnegative number."); } - this.length += length; - //add in new data - } + let bit_vec = []; + for (uint of this.bits) { + bit_vec.push(uint); + } + if (typeof data === "string") { + //string in B64 + let curr = 0; + let total_bits = 0; + let i = 0; + while (i < data.length && total_bits < length) { + let int = Base64.toInt(data[i]) + //total_bits implicitly % 32 here + curr |= (int << total_bits); - /** Creates a string version of the bit vector in B64. Does not keep the order of elements a sensible human readable format. - * - * @returns A bit vector in string format - */ - toString() { - if (this.length == 0) { - return ""; + if (total_bits % 32 > 25) { + //push and roll over uncaught bits + bit_vec.push(curr); + curr = (int >>> (32 - (total_bits % 32))); + } + + i++; + total_bits += 6; + } + + //need to push remaining bits if not pushed yet + if (total_bits % 32 <= 25) { + bit_vec.push(curr); + } + + //override length passed in if it's > length of string naturally to save space + length = Math.min(length, data.length * 6); + + } else if (typeof data === "number") { + //convert to int just in case + data = 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)) { + throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector."); + } + bit_vec.push(data); + } else { + throw new TypeError("BitVector must be appended with a Number or a B64 String"); } - + this.bits = new Uint32Array(bit_vec); + this.length += length; + //add in new data } }; From 738286275e091cde2a1daa5cd0e372f4f7584362 Mon Sep 17 00:00:00 2001 From: ferricles Date: Thu, 23 Jun 2022 11:20:23 -0700 Subject: [PATCH 06/10] toString() change, b64 constructor fix, append() functionality --- js/utils.js | 54 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/js/utils.js b/js/utils.js index 7be0df3..55e6c72 100644 --- a/js/utils.js +++ b/js/utils.js @@ -175,6 +175,10 @@ Base64 = (function () { let curr = 0; let total_bits = 0; 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 < length) { let int = Base64.toInt(data[i]) //total_bits implicitly % 32 here @@ -184,6 +188,7 @@ Base64 = (function () { //push and roll over uncaught bits bit_vec.push(curr); curr = (int >>> (32 - (total_bits % 32))); + console.log(curr); } i++; @@ -191,13 +196,9 @@ Base64 = (function () { } //need to push remaining bits if not pushed yet - if (total_bits % 32 <= 25) { + if (total_bits % 32 != 0) { bit_vec.push(curr); } - - //override length passed in if it's > length of string naturally to save space - length = Math.min(length, data.length * 6); - } else if (typeof data === "number") { //convert to int just in case data = Math.round(data); @@ -309,12 +310,12 @@ Base64 = (function () { /** Returns a BitVector in bitstring format. Probably only useful for dev debugging. * - * @returns a bit string representation of the BitVector + * @returns a bit string representation of the BitVector. Goes from higher-indexed bits to lower-indexed bits. */ toString() { let ret_str = ""; for (let i = 0; i < this.length; i++) { - ret_str += this.read_bit(i) == 0 ? "0": "1"; + ret_str = (this.read_bit(i) == 0 ? "0": "1") + ret_str; } return ret_str; } @@ -330,53 +331,64 @@ Base64 = (function () { } let bit_vec = []; - for (uint of this.bits) { + for (const uint of this.bits) { bit_vec.push(uint); } if (typeof data === "string") { //string in B64 - let curr = 0; - let total_bits = 0; + let curr = bit_vec[Math.floor(this.length / 32)]; + let total_bits = this.length; let i = 0; - while (i < data.length && total_bits < length) { + + //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 - bit_vec.push(curr); + if (bit_vec.length == (Math.floor(this.length / 32) + 1)) { + bit_vec[Math.floor(this.length / 32)] = curr; + } else { + bit_vec.push(curr); + } curr = (int >>> (32 - (total_bits % 32))); } i++; total_bits += 6; } - + //need to push remaining bits if not pushed yet - if (total_bits % 32 <= 25) { + if (total_bits % 32 != 0) { bit_vec.push(curr); } - - //override length passed in if it's > length of string naturally to save space - length = Math.min(length, data.length * 6); - } else if (typeof data === "number") { //convert to int just in case - data = Math.round(data); + 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)) { throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector."); } - bit_vec.push(data); + + //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)); + console.log((int & ~(~(0) << length))); + if (Math.floor((this.length + length) / 32) > Math.floor(this.length / 32)) { + bit_vec.push(int >>> (this.length)); + } } else { throw new TypeError("BitVector must be appended with a Number or a B64 String"); } + console.log(bit_vec); this.bits = new Uint32Array(bit_vec); this.length += length; - //add in new data } }; From bbede794b92f80a0c675f2fc92e388550a4543d7 Mon Sep 17 00:00:00 2001 From: ferricles Date: Thu, 23 Jun 2022 11:21:11 -0700 Subject: [PATCH 07/10] removed remaining printouts --- js/utils.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/js/utils.js b/js/utils.js index 55e6c72..5a56be2 100644 --- a/js/utils.js +++ b/js/utils.js @@ -188,7 +188,6 @@ Base64 = (function () { //push and roll over uncaught bits bit_vec.push(curr); curr = (int >>> (32 - (total_bits % 32))); - console.log(curr); } i++; @@ -378,7 +377,6 @@ Base64 = (function () { //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)); - console.log((int & ~(~(0) << length))); if (Math.floor((this.length + length) / 32) > Math.floor(this.length / 32)) { bit_vec.push(int >>> (this.length)); } @@ -386,7 +384,6 @@ Base64 = (function () { throw new TypeError("BitVector must be appended with a Number or a B64 String"); } - console.log(bit_vec); this.bits = new Uint32Array(bit_vec); this.length += length; } From 95cd93ad4d78f81caf1c3142a836737dbc2f431c Mon Sep 17 00:00:00 2001 From: ferricles Date: Fri, 24 Jun 2022 00:15:06 -0700 Subject: [PATCH 08/10] 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 +} From 7e82213b36eb91a3994f62d800833355e8c9ffc0 Mon Sep 17 00:00:00 2001 From: ferricles Date: Fri, 24 Jun 2022 00:29:43 -0700 Subject: [PATCH 09/10] simple range bug fix in set and clear bit --- js/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/utils.js b/js/utils.js index dd68037..0aae108 100644 --- a/js/utils.js +++ b/js/utils.js @@ -266,7 +266,7 @@ Base64 = (function () { * @param {Number} idx - The index to set. */ set_bit(idx) { - if (idx < 0 || idx > this.length) { + if (idx < 0 || idx >= this.length) { throw new RangeError("Cannot set bit outside the range of the BitVector."); } this.bits[Math.floor(idx / 32)] |= (1 << idx % 32); @@ -277,7 +277,7 @@ Base64 = (function () { * @param {Number} idx - The index to clear. */ clear_bit(idx) { - if (idx < 0 || idx > this.length) { + if (idx < 0 || idx >= this.length) { throw new RangeError("Cannot clear bit outside the range of the BitVector."); } this.bits[Math.floor(idx / 32)] &= ~(1 << idx % 32); From c7054ce25a1e7a979a35a866eb6e2d215588482f Mon Sep 17 00:00:00 2001 From: ferricles Date: Fri, 24 Jun 2022 00:43:24 -0700 Subject: [PATCH 10/10] append() number bug fix --- js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/utils.js b/js/utils.js index 0aae108..ca9687b 100644 --- a/js/utils.js +++ b/js/utils.js @@ -379,7 +379,7 @@ Base64 = (function () { //could be split between multiple new ints //reminder that shifts implicitly mod 32 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)) { + if (((this.length - 1) % 32 + 1) + length > 32) { bit_vec.push(int >>> (32 - this.length)); } } else {