mirror of
https://github.com/xuxiaobo-bobo/boda_jsEnv.git
synced 2025-04-22 18:26:01 +08:00
394 lines
13 KiB
JavaScript
394 lines
13 KiB
JavaScript
/*!
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2017-2022 Peculiar Ventures, LLC
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
const ARRAY_BUFFER_NAME = "[object ArrayBuffer]";
|
|
class BufferSourceConverter {
|
|
static isArrayBuffer(data) {
|
|
return Object.prototype.toString.call(data) === ARRAY_BUFFER_NAME;
|
|
}
|
|
static toArrayBuffer(data) {
|
|
if (this.isArrayBuffer(data)) {
|
|
return data;
|
|
}
|
|
if (data.byteLength === data.buffer.byteLength) {
|
|
return data.buffer;
|
|
}
|
|
if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) {
|
|
return data.buffer;
|
|
}
|
|
return this.toUint8Array(data.buffer)
|
|
.slice(data.byteOffset, data.byteOffset + data.byteLength)
|
|
.buffer;
|
|
}
|
|
static toUint8Array(data) {
|
|
return this.toView(data, Uint8Array);
|
|
}
|
|
static toView(data, type) {
|
|
if (data.constructor === type) {
|
|
return data;
|
|
}
|
|
if (this.isArrayBuffer(data)) {
|
|
return new type(data);
|
|
}
|
|
if (this.isArrayBufferView(data)) {
|
|
return new type(data.buffer, data.byteOffset, data.byteLength);
|
|
}
|
|
throw new TypeError("The provided value is not of type '(ArrayBuffer or ArrayBufferView)'");
|
|
}
|
|
static isBufferSource(data) {
|
|
return this.isArrayBufferView(data)
|
|
|| this.isArrayBuffer(data);
|
|
}
|
|
static isArrayBufferView(data) {
|
|
return ArrayBuffer.isView(data)
|
|
|| (data && this.isArrayBuffer(data.buffer));
|
|
}
|
|
static isEqual(a, b) {
|
|
const aView = BufferSourceConverter.toUint8Array(a);
|
|
const bView = BufferSourceConverter.toUint8Array(b);
|
|
if (aView.length !== bView.byteLength) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < aView.length; i++) {
|
|
if (aView[i] !== bView[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
static concat(...args) {
|
|
let buffers;
|
|
if (Array.isArray(args[0]) && !(args[1] instanceof Function)) {
|
|
buffers = args[0];
|
|
}
|
|
else if (Array.isArray(args[0]) && args[1] instanceof Function) {
|
|
buffers = args[0];
|
|
}
|
|
else {
|
|
if (args[args.length - 1] instanceof Function) {
|
|
buffers = args.slice(0, args.length - 1);
|
|
}
|
|
else {
|
|
buffers = args;
|
|
}
|
|
}
|
|
let size = 0;
|
|
for (const buffer of buffers) {
|
|
size += buffer.byteLength;
|
|
}
|
|
const res = new Uint8Array(size);
|
|
let offset = 0;
|
|
for (const buffer of buffers) {
|
|
const view = this.toUint8Array(buffer);
|
|
res.set(view, offset);
|
|
offset += view.length;
|
|
}
|
|
if (args[args.length - 1] instanceof Function) {
|
|
return this.toView(res, args[args.length - 1]);
|
|
}
|
|
return res.buffer;
|
|
}
|
|
}
|
|
|
|
const STRING_TYPE = "string";
|
|
const HEX_REGEX = /^[0-9a-f]+$/i;
|
|
const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
const BASE64URL_REGEX = /^[a-zA-Z0-9-_]+$/;
|
|
class Utf8Converter {
|
|
static fromString(text) {
|
|
const s = unescape(encodeURIComponent(text));
|
|
const uintArray = new Uint8Array(s.length);
|
|
for (let i = 0; i < s.length; i++) {
|
|
uintArray[i] = s.charCodeAt(i);
|
|
}
|
|
return uintArray.buffer;
|
|
}
|
|
static toString(buffer) {
|
|
const buf = BufferSourceConverter.toUint8Array(buffer);
|
|
let encodedString = "";
|
|
for (let i = 0; i < buf.length; i++) {
|
|
encodedString += String.fromCharCode(buf[i]);
|
|
}
|
|
const decodedString = decodeURIComponent(escape(encodedString));
|
|
return decodedString;
|
|
}
|
|
}
|
|
class Utf16Converter {
|
|
static toString(buffer, littleEndian = false) {
|
|
const arrayBuffer = BufferSourceConverter.toArrayBuffer(buffer);
|
|
const dataView = new DataView(arrayBuffer);
|
|
let res = "";
|
|
for (let i = 0; i < arrayBuffer.byteLength; i += 2) {
|
|
const code = dataView.getUint16(i, littleEndian);
|
|
res += String.fromCharCode(code);
|
|
}
|
|
return res;
|
|
}
|
|
static fromString(text, littleEndian = false) {
|
|
const res = new ArrayBuffer(text.length * 2);
|
|
const dataView = new DataView(res);
|
|
for (let i = 0; i < text.length; i++) {
|
|
dataView.setUint16(i * 2, text.charCodeAt(i), littleEndian);
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
class Convert {
|
|
static isHex(data) {
|
|
return typeof data === STRING_TYPE
|
|
&& HEX_REGEX.test(data);
|
|
}
|
|
static isBase64(data) {
|
|
return typeof data === STRING_TYPE
|
|
&& BASE64_REGEX.test(data);
|
|
}
|
|
static isBase64Url(data) {
|
|
return typeof data === STRING_TYPE
|
|
&& BASE64URL_REGEX.test(data);
|
|
}
|
|
static ToString(buffer, enc = "utf8") {
|
|
const buf = BufferSourceConverter.toUint8Array(buffer);
|
|
switch (enc.toLowerCase()) {
|
|
case "utf8":
|
|
return this.ToUtf8String(buf);
|
|
case "binary":
|
|
return this.ToBinary(buf);
|
|
case "hex":
|
|
return this.ToHex(buf);
|
|
case "base64":
|
|
return this.ToBase64(buf);
|
|
case "base64url":
|
|
return this.ToBase64Url(buf);
|
|
case "utf16le":
|
|
return Utf16Converter.toString(buf, true);
|
|
case "utf16":
|
|
case "utf16be":
|
|
return Utf16Converter.toString(buf);
|
|
default:
|
|
throw new Error(`Unknown type of encoding '${enc}'`);
|
|
}
|
|
}
|
|
static FromString(str, enc = "utf8") {
|
|
if (!str) {
|
|
return new ArrayBuffer(0);
|
|
}
|
|
switch (enc.toLowerCase()) {
|
|
case "utf8":
|
|
return this.FromUtf8String(str);
|
|
case "binary":
|
|
return this.FromBinary(str);
|
|
case "hex":
|
|
return this.FromHex(str);
|
|
case "base64":
|
|
return this.FromBase64(str);
|
|
case "base64url":
|
|
return this.FromBase64Url(str);
|
|
case "utf16le":
|
|
return Utf16Converter.fromString(str, true);
|
|
case "utf16":
|
|
case "utf16be":
|
|
return Utf16Converter.fromString(str);
|
|
default:
|
|
throw new Error(`Unknown type of encoding '${enc}'`);
|
|
}
|
|
}
|
|
static ToBase64(buffer) {
|
|
const buf = BufferSourceConverter.toUint8Array(buffer);
|
|
if (typeof btoa !== "undefined") {
|
|
const binary = this.ToString(buf, "binary");
|
|
return btoa(binary);
|
|
}
|
|
else {
|
|
return Buffer.from(buf).toString("base64");
|
|
}
|
|
}
|
|
static FromBase64(base64) {
|
|
const formatted = this.formatString(base64);
|
|
if (!formatted) {
|
|
return new ArrayBuffer(0);
|
|
}
|
|
if (!Convert.isBase64(formatted)) {
|
|
throw new TypeError("Argument 'base64Text' is not Base64 encoded");
|
|
}
|
|
if (typeof atob !== "undefined") {
|
|
return this.FromBinary(atob(formatted));
|
|
}
|
|
else {
|
|
return new Uint8Array(Buffer.from(formatted, "base64")).buffer;
|
|
}
|
|
}
|
|
static FromBase64Url(base64url) {
|
|
const formatted = this.formatString(base64url);
|
|
if (!formatted) {
|
|
return new ArrayBuffer(0);
|
|
}
|
|
if (!Convert.isBase64Url(formatted)) {
|
|
throw new TypeError("Argument 'base64url' is not Base64Url encoded");
|
|
}
|
|
return this.FromBase64(this.Base64Padding(formatted.replace(/\-/g, "+").replace(/\_/g, "/")));
|
|
}
|
|
static ToBase64Url(data) {
|
|
return this.ToBase64(data).replace(/\+/g, "-").replace(/\//g, "_").replace(/\=/g, "");
|
|
}
|
|
static FromUtf8String(text, encoding = Convert.DEFAULT_UTF8_ENCODING) {
|
|
switch (encoding) {
|
|
case "ascii":
|
|
return this.FromBinary(text);
|
|
case "utf8":
|
|
return Utf8Converter.fromString(text);
|
|
case "utf16":
|
|
case "utf16be":
|
|
return Utf16Converter.fromString(text);
|
|
case "utf16le":
|
|
case "usc2":
|
|
return Utf16Converter.fromString(text, true);
|
|
default:
|
|
throw new Error(`Unknown type of encoding '${encoding}'`);
|
|
}
|
|
}
|
|
static ToUtf8String(buffer, encoding = Convert.DEFAULT_UTF8_ENCODING) {
|
|
switch (encoding) {
|
|
case "ascii":
|
|
return this.ToBinary(buffer);
|
|
case "utf8":
|
|
return Utf8Converter.toString(buffer);
|
|
case "utf16":
|
|
case "utf16be":
|
|
return Utf16Converter.toString(buffer);
|
|
case "utf16le":
|
|
case "usc2":
|
|
return Utf16Converter.toString(buffer, true);
|
|
default:
|
|
throw new Error(`Unknown type of encoding '${encoding}'`);
|
|
}
|
|
}
|
|
static FromBinary(text) {
|
|
const stringLength = text.length;
|
|
const resultView = new Uint8Array(stringLength);
|
|
for (let i = 0; i < stringLength; i++) {
|
|
resultView[i] = text.charCodeAt(i);
|
|
}
|
|
return resultView.buffer;
|
|
}
|
|
static ToBinary(buffer) {
|
|
const buf = BufferSourceConverter.toUint8Array(buffer);
|
|
let res = "";
|
|
for (let i = 0; i < buf.length; i++) {
|
|
res += String.fromCharCode(buf[i]);
|
|
}
|
|
return res;
|
|
}
|
|
static ToHex(buffer) {
|
|
const buf = BufferSourceConverter.toUint8Array(buffer);
|
|
let result = "";
|
|
const len = buf.length;
|
|
for (let i = 0; i < len; i++) {
|
|
const byte = buf[i];
|
|
if (byte < 16) {
|
|
result += "0";
|
|
}
|
|
result += byte.toString(16);
|
|
}
|
|
return result;
|
|
}
|
|
static FromHex(hexString) {
|
|
let formatted = this.formatString(hexString);
|
|
if (!formatted) {
|
|
return new ArrayBuffer(0);
|
|
}
|
|
if (!Convert.isHex(formatted)) {
|
|
throw new TypeError("Argument 'hexString' is not HEX encoded");
|
|
}
|
|
if (formatted.length % 2) {
|
|
formatted = `0${formatted}`;
|
|
}
|
|
const res = new Uint8Array(formatted.length / 2);
|
|
for (let i = 0; i < formatted.length; i = i + 2) {
|
|
const c = formatted.slice(i, i + 2);
|
|
res[i / 2] = parseInt(c, 16);
|
|
}
|
|
return res.buffer;
|
|
}
|
|
static ToUtf16String(buffer, littleEndian = false) {
|
|
return Utf16Converter.toString(buffer, littleEndian);
|
|
}
|
|
static FromUtf16String(text, littleEndian = false) {
|
|
return Utf16Converter.fromString(text, littleEndian);
|
|
}
|
|
static Base64Padding(base64) {
|
|
const padCount = 4 - (base64.length % 4);
|
|
if (padCount < 4) {
|
|
for (let i = 0; i < padCount; i++) {
|
|
base64 += "=";
|
|
}
|
|
}
|
|
return base64;
|
|
}
|
|
static formatString(data) {
|
|
return (data === null || data === void 0 ? void 0 : data.replace(/[\n\r\t ]/g, "")) || "";
|
|
}
|
|
}
|
|
Convert.DEFAULT_UTF8_ENCODING = "utf8";
|
|
|
|
function assign(target, ...sources) {
|
|
const res = arguments[0];
|
|
for (let i = 1; i < arguments.length; i++) {
|
|
const obj = arguments[i];
|
|
for (const prop in obj) {
|
|
res[prop] = obj[prop];
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
function combine(...buf) {
|
|
const totalByteLength = buf.map((item) => item.byteLength).reduce((prev, cur) => prev + cur);
|
|
const res = new Uint8Array(totalByteLength);
|
|
let currentPos = 0;
|
|
buf.map((item) => new Uint8Array(item)).forEach((arr) => {
|
|
for (const item2 of arr) {
|
|
res[currentPos++] = item2;
|
|
}
|
|
});
|
|
return res.buffer;
|
|
}
|
|
function isEqual(bytes1, bytes2) {
|
|
if (!(bytes1 && bytes2)) {
|
|
return false;
|
|
}
|
|
if (bytes1.byteLength !== bytes2.byteLength) {
|
|
return false;
|
|
}
|
|
const b1 = new Uint8Array(bytes1);
|
|
const b2 = new Uint8Array(bytes2);
|
|
for (let i = 0; i < bytes1.byteLength; i++) {
|
|
if (b1[i] !== b2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export { BufferSourceConverter, Convert, assign, combine, isEqual };
|