Skip to content

Commit 3da69eb

Browse files
committed
fix z85 implementation
1 parent 90e6018 commit 3da69eb

File tree

1 file changed

+38
-104
lines changed

1 file changed

+38
-104
lines changed

src/z85.js

Lines changed: 38 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,57 @@
1-
const CHARSET = new Uint8Array([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 46, 45, 58, 43, 61, 94, 33, 47, 42, 63, 38, 60, 62, 40, 41, 91, 93, 123, 125, 64, 37, 36, 35]);
2-
const POW_85 = [1, 85, 7225, 614125, 52200625];
3-
/** @type {Uint8Array} */
4-
let REVERSE_MAP;
1+
const ENCODE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#";
2+
const DECODE = [-1, 68, -1, 84, 83, 82, 72, -1, 75, 76, 70, 65, -1, 63, 62, 69, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64, -1, 73, 66, 74, 71, 81, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, -1, 78, 67, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 79, -1, 80, -1, -1];
3+
const POW_85 = [0, 1, 85, 7225, 614125, 52200625];
4+
const POW_256 = [1, 256, 65536, 16777216];
55

66
/**
77
* Encodes binary data into a Z85 string.
88
* @param {ArrayBuffer} data - The binary data to encode
99
* @returns {string} The Z85 encoded string
1010
*/
11-
export function encode85(data) {
12-
const byteLength = data.byteLength;
13-
const fullBlocks = Math.floor(byteLength / 4);
14-
const remain = byteLength % 4;
15-
const outputLength = Math.ceil(byteLength * 5 / 4);
16-
const target = new Uint8Array(outputLength);
11+
function encode85(data) {
1712
const dv = new DataView(data);
18-
19-
let targetIndex = 0;
20-
21-
// Process complete 4-byte blocks
22-
for (let i = 0; i < fullBlocks; i++) {
23-
let value = dv.getUint32(i * 4);
24-
25-
// Encode 5 characters
26-
target[targetIndex + 4] = CHARSET[value % 85];
27-
value = Math.floor(value / 85);
28-
target[targetIndex + 3] = CHARSET[value % 85];
29-
value = Math.floor(value / 85);
30-
target[targetIndex + 2] = CHARSET[value % 85];
31-
value = Math.floor(value / 85);
32-
target[targetIndex + 1] = CHARSET[value % 85];
33-
target[targetIndex] = CHARSET[Math.floor(value / 85)];
34-
35-
targetIndex += 5;
36-
}
37-
38-
// Handle remaining bytes
39-
if (remain) {
40-
let value = 0;
41-
for (let i = 0; i < remain; i++) {
42-
value = (value << 8) | dv.getUint8(fullBlocks * 4 + i);
43-
}
44-
value <<= (4 - remain) * 8; // Pad remaining bytes
45-
46-
const partialOutput = [];
47-
for (let i = 0; i < Math.ceil((remain + 1) * 5 / 4); i++) {
48-
partialOutput.unshift(CHARSET[value % 85]);
49-
value = Math.floor(value / 85);
50-
}
51-
52-
for (const char of partialOutput) {
53-
target[targetIndex++] = char;
13+
const length = dv.byteLength;
14+
const padding = (4 - (length % 4)) % 4;
15+
16+
let result = '', value = 0;
17+
for (let i = 0; i < length + padding; ++i) {
18+
const isPadding = i >= length;
19+
value = value * 256 + (isPadding ? 0 : dv.getUint8(i));
20+
if ((i + 1) % 4 === 0) {
21+
for (let j = 5; j > 0; --j) {
22+
if (isPadding && j <= padding)
23+
continue;
24+
25+
result += ENCODE[Math.floor(value / POW_85[j]) % 85];
26+
}
27+
value = 0;
5428
}
5529
}
5630

57-
return new TextDecoder().decode(target);
58-
}
59-
60-
/**
61-
* Creates a reverse mapping from character codes to indices.
62-
* @param {Uint8Array} mapOrig - The original character map
63-
* @returns {Uint8Array} A mapping of character codes to indices
64-
* @private
65-
*/
66-
function getReverseMap(mapOrig) {
67-
const revMap = new Uint8Array(128);
68-
for (const [num, charCode] of Object.entries(mapOrig)) {
69-
revMap[charCode] = parseInt(num);
70-
}
71-
return revMap;
72-
}
31+
return result;
32+
};
7333

7434
/**
7535
* Decodes a Z85 string into binary data.
7636
* @param {string} string - The Z85 encoded string
7737
* @returns {ArrayBuffer} The decoded binary data
7838
*/
79-
export function decode85(string) {
80-
if (!REVERSE_MAP) REVERSE_MAP = getReverseMap(CHARSET);
81-
const z85ab = new Uint8Array(string.length);
82-
for (let i = 0; i < string.length; i++) {
83-
z85ab[i] = string.charCodeAt(i);
84-
}
85-
86-
const pad = (5 - (z85ab.length % 5)) % 5;
87-
const result = new Uint8Array((Math.ceil(z85ab.length / 5) * 4) - pad);
88-
const dv = new DataView(result.buffer);
89-
90-
// Process complete 5-character blocks
91-
const completeBlocks = Math.floor(z85ab.length / 5) - 1;
92-
for (let i = 0; i <= completeBlocks; i++) {
93-
const chunk = z85ab.slice(i * 5, i * 5 + 5);
94-
const value = chunk.reduceRight((acc, char, idx) => {
95-
return acc + REVERSE_MAP[char] * POW_85[4 - idx];
96-
}, 0);
97-
dv.setUint32(i * 4, value);
98-
}
99-
100-
// Handle remaining characters
101-
if (pad > 0) {
102-
const lastIndex = completeBlocks + 1;
103-
const lastChar = CHARSET[CHARSET.length - 1];
104-
const lastPart = new Uint8Array([
105-
...z85ab.slice(lastIndex * 5),
106-
lastChar, lastChar, lastChar, lastChar
107-
]);
108-
109-
const value = [...lastPart].slice(0, 5).reduceRight((acc, char, idx) => {
110-
return acc + REVERSE_MAP[char] * POW_85[4 - idx];
111-
}, 0);
112-
113-
const lastDv = new DataView(lastPart.buffer);
114-
lastDv.setUint32(0, value);
115-
116-
const remainingBytes = 4 - pad;
117-
for (let j = 0; j < remainingBytes; j++) {
118-
result[lastIndex * 4 + j] = lastPart[j];
119-
}
39+
function decode85(string) {
40+
const remainder = string.length % 5;
41+
const padding = 5 - (remainder === 0 ? 5 : remainder);
42+
string = string.padEnd(string.length + padding, ENCODE[ENCODE.length - 1]);
43+
const length = string.length;
44+
45+
let buffer = new Uint8Array((length * 4 / 5) - padding);
46+
let value = 0, char = 0, byte = 0;
47+
for (let i = 0; i < length; ++i) {
48+
value = value * 85 + DECODE[string.charCodeAt(char++) - 32];
49+
if (char % 5 !== 0) continue;
50+
51+
for (let j = 3; j >= 0; --j)
52+
buffer[byte++] = Math.floor(value / POW_256[j]) % 256;
53+
value = 0;
12054
}
12155

122-
return result.buffer;
56+
return buffer.buffer;
12357
}

0 commit comments

Comments
 (0)