|
1 | | -export { |
2 | | - encode, decode, BIT_LENGTH, ALPHABET_LENGTH,// public |
| 1 | +export { |
| 2 | + // public |
| 3 | + encode, decode, |
| 4 | + BIT_LENGTH, ALPHABET_LENGTH, |
| 5 | + // private |
3 | 6 | _0To31NumberFromCharacter, |
4 | 7 | _0To31NumbersFromString, |
5 | 8 | _compactUInt8ArrayFrom0To31Numbers, |
@@ -54,47 +57,47 @@ const _compactUInt8ArrayFrom0To31Numbers = _0To31Numbers => { |
54 | 57 | const compactUInt8Array = new Uint8Array(byteLength); |
55 | 58 |
|
56 | 59 | let bitPosition = 0; |
57 | | - let currentByte = 0; |
| 60 | + let currentNumberToBeSaved = 0; |
58 | 61 | let byteIndex = 0; |
59 | | - let lastPrintNeeded = false; |
| 62 | + |
60 | 63 | const saveByte = (number) => { |
61 | | - console.log(`save ${number} (${number.toString(2)})`) |
| 64 | + console.log(`save ${number} (${number.toString(2).padStart(8, "0")})`) |
62 | 65 | compactUInt8Array[byteIndex] = number; |
63 | 66 | byteIndex += 1; |
64 | | - currentByte = 0; |
| 67 | + currentNumberToBeSaved = 0; |
65 | 68 | bitPosition = 0; |
66 | 69 | }; |
67 | 70 | _0To31Numbers.forEach(number => { |
68 | | - if (bitPosition + BIT_LENGTH < BYTE) { // can fit at once |
| 71 | + if (bitPosition + BIT_LENGTH <= BYTE) { // can fit at once |
69 | 72 | const toShift = BYTE - BIT_LENGTH - bitPosition; |
70 | | - currentByte += number << toShift; |
| 73 | + currentNumberToBeSaved += number << toShift; |
71 | 74 | bitPosition += BIT_LENGTH; |
72 | 75 |
|
73 | 76 | if (bitPosition === BYTE) { |
74 | | - saveByte(currentByte); |
| 77 | + saveByte(currentNumberToBeSaved); |
75 | 78 | } |
76 | 79 | } else { // cannot fit at once |
77 | 80 | const shiftRight = -BYTE + bitPosition + BIT_LENGTH; // 2 |
78 | 81 | const missedBits = BIT_LENGTH - shiftRight; // 3 |
79 | 82 | const clamped = number >> shiftRight; |
80 | | - currentByte += clamped; |
| 83 | + currentNumberToBeSaved += clamped; |
81 | 84 |
|
82 | | - saveByte(currentByte); |
| 85 | + saveByte(currentNumberToBeSaved); |
83 | 86 |
|
84 | 87 | const substractHead = clamped << shiftRight; |
85 | 88 | const rest = number - substractHead; |
86 | | - currentByte += rest << (BYTE+1) - missedBits; |
87 | | - bitPosition += missedBits; |
| 89 | + currentNumberToBeSaved += rest << (BYTE+1) - missedBits; |
| 90 | + bitPosition += missedBits-1; //3 |
88 | 91 | } |
89 | 92 | }); |
90 | 93 | // todo handle all cases |
91 | | - console.log(90,currentByte) |
92 | | - if (currentByte) { |
93 | | - saveByte(currentByte); |
| 94 | + // console.log(90,currentNumberToBeSaved) |
| 95 | + if (currentNumberToBeSaved) { |
| 96 | + saveByte(currentNumberToBeSaved); |
94 | 97 | } //else { |
95 | 98 | // compactUInt8Array[byteIndex] = remainder; |
96 | 99 | // } |
97 | | - console.log(96,compactUInt8Array) |
| 100 | + console.log("3_compactUInt8ArrayFrom0To31Numbers RESULT",compactUInt8Array) |
98 | 101 | return compactUInt8Array; |
99 | 102 | }; |
100 | 103 |
|
@@ -148,13 +151,63 @@ const _0To31NumbersFromCompactUInt8Array = compactUInt8Array => { |
148 | 151 | return _0To31Numbers; |
149 | 152 | }; |
150 | 153 |
|
| 154 | +const _bytesRequiredToSaveNumber = number => { |
| 155 | + // maybe there is a simpler way |
| 156 | + let bits = 0; |
| 157 | + let divided = number; |
| 158 | + while (divided > 0) { |
| 159 | + bits += 1; |
| 160 | + divided = divided >> 1; |
| 161 | + } |
| 162 | + const bytesRequired = Math.ceil(bits / BYTE); |
| 163 | + console.log("bits required", bits); |
| 164 | + console.log("bytes required", bytesRequired); |
| 165 | + return bytesRequired; |
| 166 | +}; |
| 167 | + |
| 168 | +const _initializeUint8WithLength = characters => { |
| 169 | + const textBitLength = characters * BIT_LENGTH; |
| 170 | + const textByteLength = Math.ceil(textBitLength / BYTE); |
| 171 | + const bytesRequiredForTextByteLength = _bytesRequiredToSaveNumber(textByteLength); |
| 172 | + let totalByteLength = 1 + bytesRequiredForTextByteLength + textByteLength; |
| 173 | + let ArrayConstructor; |
| 174 | + if (bytesRequiredForTextByteLength === 1) { |
| 175 | + ArrayConstructor = Uint8Array; |
| 176 | + } else if (bytesRequiredForTextByteLength === 2) { |
| 177 | + ArrayConstructor = Uint16Array; |
| 178 | + totalByteLength += 1; // see todo |
| 179 | + } else if (bytesRequiredForTextByteLength === 3) { |
| 180 | + console.warn(`unhandled case byte ${Byte} === 3`); |
| 181 | + } else if (bytesRequiredForTextByteLength === 4) { |
| 182 | + ArrayConstructor = Uint32Array; |
| 183 | + totalByteLength += 3; // see todo |
| 184 | + } else if (bytesRequiredForTextByteLength > 4) { |
| 185 | + console.warn(`unhandled case byte ${Byte} > 4`); |
| 186 | + } |
| 187 | + const uInt8WithLength = new Uint8Array(totalByteLength); |
| 188 | + uInt8WithLength[0] = bytesRequiredForTextByteLength; |
| 189 | + const tempArray = new ArrayConstructor(uInt8WithLength.buffer); |
| 190 | + // todo : solve problem this can leave empty spaces |
| 191 | + tempArray[1] = textByteLength; |
| 192 | + const offset = totalByteLength - textByteLength; |
| 193 | + return [uInt8WithLength, offset]; |
| 194 | + |
| 195 | +}; |
| 196 | + |
| 197 | + |
| 198 | +const _joinUint8Array = function (target, copyInto, byteOffset) { |
| 199 | + target.set(copyInto, byteOffset); |
| 200 | + return target; |
| 201 | +}; |
| 202 | + |
151 | 203 | const encode = string => { |
| 204 | + const { length } = string; |
152 | 205 | const _0To31Numbers = _0To31NumbersFromString(string); |
| 206 | + const [uInt8WithLength, offset] = _initializeUint8WithLength(length); |
153 | 207 | const uInt8Array = _compactUInt8ArrayFrom0To31Numbers(_0To31Numbers); |
154 | | - return uInt8Array; |
| 208 | + return _joinUint8Array(uInt8WithLength, uInt8Array, offset); |
155 | 209 | }; |
156 | 210 |
|
157 | | - |
158 | 211 | const decode = uInt8Array => { |
159 | 212 | const _0To31Numbers = _0To31NumbersFromCompactUInt8Array(uInt8Array); |
160 | 213 | const string = stringFrom0To31Numbers(_0To31Numbers); |
|
0 commit comments