WXL
4 天以前 2cc85c64f1c64a2dbaeae276a3e2ca8420de76b7
1
2
3
4
5
6
7
8
9
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
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
62
63
64
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
91
92
93
94
95
96
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
    MIT License http://www.opensource.org/licenses/mit-license.php
    Author Alexander Akait @alexander-akait
*/
 
"use strict";
 
/** @typedef {import("../Hash")} Hash */
/** @typedef {import("../../../declarations/WebpackOptions").HashDigest} Encoding */
 
/** @typedef {"26" | "32" | "36" | "49" | "52" | "58" | "62"} Base */
 
/* cSpell:disable */
 
/** @type {Record<Base, string>} */
const ENCODE_TABLE = Object.freeze({
    26: "abcdefghijklmnopqrstuvwxyz",
    32: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
    36: "0123456789abcdefghijklmnopqrstuvwxyz",
    49: "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ",
    52: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
    58: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
    62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
});
 
/* cSpell:enable */
 
const ZERO = BigInt("0");
const EIGHT = BigInt("8");
const FF = BigInt("0xff");
 
/**
 * It encodes octet arrays by doing long divisions on all significant digits in the array, creating a representation of that number in the new base.
 * Then for every leading zero in the input (not significant as a number) it will encode as a single leader character.
 * This is the first in the alphabet and will decode as 8 bits. The other characters depend upon the base.
 * For example, a base58 alphabet packs roughly 5.858 bits per character.
 * This means the encoded string 000f (using a base16, 0-f alphabet) will actually decode to 4 bytes unlike a canonical hex encoding which uniformly packs 4 bits into each character.
 * While unusual, this does mean that no padding is required, and it works for bases like 43.
 * @param {Buffer} buffer buffer
 * @param {Base} base base
 * @returns {string} encoded buffer
 */
const encode = (buffer, base) => {
    if (buffer.length === 0) return "";
    const bigIntBase = BigInt(ENCODE_TABLE[base].length);
    // Convert buffer to BigInt efficiently using bitwise operations
    let value = ZERO;
    for (let i = 0; i < buffer.length; i++) {
        value = (value << EIGHT) | BigInt(buffer[i]);
    }
    // Convert to baseX string efficiently using array
    /** @type {string[]} */
    const digits = [];
    if (value === ZERO) return ENCODE_TABLE[base][0];
    while (value > ZERO) {
        const remainder = Number(value % bigIntBase);
        digits.push(ENCODE_TABLE[base][remainder]);
        value /= bigIntBase;
    }
    return digits.reverse().join("");
};
 
/**
 * Returns buffer.
 * @param {string} data string
 * @param {Base} base base
 * @returns {Buffer} buffer
 */
const decode = (data, base) => {
    if (data.length === 0) return Buffer.from("");
    const bigIntBase = BigInt(ENCODE_TABLE[base].length);
    // Convert the baseX string to a BigInt value
    let value = ZERO;
    for (let i = 0; i < data.length; i++) {
        const digit = ENCODE_TABLE[base].indexOf(data[i]);
        if (digit === -1) {
            throw new Error(`Invalid character at position ${i}: ${data[i]}`);
        }
        value = value * bigIntBase + BigInt(digit);
    }
    // If value is 0, return a single-byte buffer with value 0
    if (value === ZERO) {
        return Buffer.alloc(1);
    }
    // Determine buffer size efficiently by counting bytes
    let temp = value;
    let byteLength = 0;
    while (temp > ZERO) {
        temp >>= EIGHT;
        byteLength++;
    }
    // Create buffer and fill it from right to left
    const buffer = Buffer.alloc(byteLength);
    for (let i = byteLength - 1; i >= 0; i--) {
        buffer[i] = Number(value & FF);
        value >>= EIGHT;
    }
    return buffer;
};
 
// Compatibility with the old hash libraries, they can return different structures, so let's stringify them firstly
 
/**
 * Returns a string representation.
 * @param {string | { toString: (radix: number) => string }} value value
 * @param {string} encoding encoding
 * @returns {string} string
 */
const toString = (value, encoding) =>
    typeof value === "string"
        ? value
        : Buffer.from(value.toString(16), "hex").toString(
                /** @type {NodeJS.BufferEncoding} */
                (encoding)
            );
 
/**
 * Returns buffer.
 * @param {Buffer | { toString: (radix: number) => string }} value value
 * @returns {Buffer} buffer
 */
const toBuffer = (value) =>
    Buffer.isBuffer(value) ? value : Buffer.from(value.toString(16), "hex");
 
let isBase64URLSupported = false;
 
try {
    isBase64URLSupported = Boolean(Buffer.from("", "base64url"));
} catch (_err) {
    // Nothing
}
 
/**
 * Processes the provided hash.
 * @param {Hash} hash hash
 * @param {string | Buffer} data data
 * @param {Encoding=} encoding encoding of the return value
 * @returns {void}
 */
const update = (hash, data, encoding) => {
    if (encoding === "base64url" && !isBase64URLSupported) {
        const base64String = /** @type {string} */ (data)
            .replace(/-/g, "+")
            .replace(/_/g, "/");
        const buf = Buffer.from(base64String, "base64");
        hash.update(buf);
        return;
    } else if (
        typeof data === "string" &&
        encoding &&
        typeof ENCODE_TABLE[/** @type {Base} */ (encoding.slice(4))] !== "undefined"
    ) {
        const buf = decode(data, /** @type {Base} */ (encoding.slice(4)));
        hash.update(buf);
        return;
    }
 
    if (encoding) {
        hash.update(/** @type {string} */ (data), encoding);
    } else {
        hash.update(data);
    }
};
 
/**
 * Returns digest.
 * @overload
 * @param {Hash} hash hash
 * @returns {Buffer} digest
 */
/**
 * Returns digest.
 * @overload
 * @param {Hash} hash hash
 * @param {undefined} encoding encoding of the return value
 * @param {boolean=} isSafe true when we await right types from digest(), otherwise false
 * @returns {Buffer} digest
 */
/**
 * Returns digest.
 * @overload
 * @param {Hash} hash hash
 * @param {Encoding} encoding encoding of the return value
 * @param {boolean=} isSafe true when we await right types from digest(), otherwise false
 * @returns {string} digest
 */
/**
 * Returns digest.
 * @param {Hash} hash hash
 * @param {Encoding=} encoding encoding of the return value
 * @param {boolean=} isSafe true when we await right types from digest(), otherwise false
 * @returns {string | Buffer} digest
 */
const digest = (hash, encoding, isSafe) => {
    if (typeof encoding === "undefined") {
        return isSafe ? hash.digest() : toBuffer(hash.digest());
    }
 
    if (encoding === "base64url" && !isBase64URLSupported) {
        const digest = isSafe
            ? hash.digest("base64")
            : toString(hash.digest("base64"), "base64");
 
        return digest.replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]+$/, "");
    } else if (
        typeof ENCODE_TABLE[/** @type {Base} */ (encoding.slice(4))] !== "undefined"
    ) {
        const buf = isSafe ? hash.digest() : toBuffer(hash.digest());
 
        return encode(
            buf,
            /** @type {Base} */
            (encoding.slice(4))
        );
    }
 
    return isSafe
        ? hash.digest(encoding)
        : toString(hash.digest(encoding), encoding);
};
 
module.exports.decode = decode;
module.exports.digest = digest;
module.exports.encode = encode;
module.exports.update = update;