Home Reference Source Test

src/main/generic/utils/BufferUtils.js

class BufferUtils {
    static _codePointTextDecoder(u8) {
        if (typeof TextDecoder === 'undefined') throw new Error('TextDecoder not supported');
        if (BufferUtils._ISO_8859_15_DECODER === null) throw new Error('TextDecoder does not supprot iso-8859-15');
        if (BufferUtils._ISO_8859_15_DECODER === undefined) {
            try {
                BufferUtils._ISO_8859_15_DECODER = new TextDecoder('iso-8859-15');
            } finally {
                BufferUtils._ISO_8859_15_DECODER = null;
            }
        }
        return BufferUtils._ISO_8859_15_DECODER.decode(u8)
            .replace('€', '¤').replace('Š', '¦').replace('š', '¨').replace('Ž', '´')
            .replace('ž', '¸').replace('Œ', '¼').replace('œ', '½').replace('Ÿ', '¾');
    }

    static _tripletToBase64(num) {
        return BufferUtils._BASE64_LOOKUP[num >> 18 & 0x3F] + BufferUtils._BASE64_LOOKUP[num >> 12 & 0x3F] + BufferUtils._BASE64_LOOKUP[num >> 6 & 0x3F] + BufferUtils._BASE64_LOOKUP[num & 0x3F];
    }

    static _base64encodeChunk(u8, start, end) {
        let tmp;
        const output = [];
        for (let i = start; i < end; i += 3) {
            tmp = ((u8[i] << 16) & 0xFF0000) + ((u8[i + 1] << 8) & 0xFF00) + (u8[i + 2] & 0xFF);
            output.push(BufferUtils._tripletToBase64(tmp));
        }
        return output.join('');
    }

    static _base64fromByteArray(u8) {
        let tmp;
        const len = u8.length;
        const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
        let output = '';
        const parts = [];
        const maxChunkLength = 16383; // must be multiple of 3

        // go through the array every three bytes, we'll deal with trailing stuff later
        for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
            parts.push(BufferUtils._base64encodeChunk(u8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)));
        }

        // pad the end with zeros, but make sure to not forget the extra bytes
        if (extraBytes === 1) {
            tmp = u8[len - 1];
            output += BufferUtils._BASE64_LOOKUP[tmp >> 2];
            output += BufferUtils._BASE64_LOOKUP[(tmp << 4) & 0x3F];
            output += '==';
        } else if (extraBytes === 2) {
            tmp = (u8[len - 2] << 8) + (u8[len - 1]);
            output += BufferUtils._BASE64_LOOKUP[tmp >> 10];
            output += BufferUtils._BASE64_LOOKUP[(tmp >> 4) & 0x3F];
            output += BufferUtils._BASE64_LOOKUP[(tmp << 2) & 0x3F];
            output += '=';
        }

        parts.push(output);

        return parts.join('');
    }

    /**
     * @param {*} buffer
     * @return {string}
     */
    static toBase64(buffer) {
        if (typeof Buffer !== 'undefined' && typeof window === 'undefined') {
            return Buffer.from(buffer).toString('base64');
        } else if (typeof TextDecoder !== 'undefined' && BufferUtils._ISO_8859_15_DECODER !== null) {
            try {
                return btoa(BufferUtils._codePointTextDecoder(new Uint8Array(buffer)));
            } catch (e) {
                // Disabled itself
            }
        }

        return BufferUtils._base64fromByteArray(new Uint8Array(buffer));
    }

    /**
     * @param {string} base64
     * @return {Uint8Array}
     */
    static fromBase64(base64) {
        if (!(/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/.test(base64))) {
            throw new Error('Invalid base64');
        }
        return Uint8Array.from(atob(base64), c => c.charCodeAt(0));
    }

    /**
     * @param {*} buffer
     * @return {string}
     */
    static toBase64lex(buffer) {
        const base64 = BufferUtils.toBase64(buffer);
        let base64lex = '';
        for (let i = 0; i < base64.length; i++) {
            base64lex += BufferUtils.BASE64_TO_BASE64_LEX[base64[i]];
        }
        return base64lex;
    }

    /**
     * @param {string} base64lex
     * @return {Uint8Array}
     */
    static fromBase64lex(base64lex) {
        let base64 = '';
        for (let i = 0; i < base64lex.length; i++) {
            base64 += BufferUtils.BASE64_LEX_TO_BASE64[base64lex[i]];
        }
        return BufferUtils.fromBase64(base64);
    }

    /**
     * @param {*} a
     * @param {*} b
     * @return {boolean}
     */
    static equals(a, b) {
        if (a.length !== b.length) return false;
        const viewA = new Uint8Array(a);
        const viewB = new Uint8Array(b);
        for (let i = 0; i < a.length; i++) {
            if (viewA[i] !== viewB[i]) return false;
        }
        return true;
    }

    /**
     * @param {*} a
     * @param {*} b
     * @return {number} -1 if a is smaller than b, 1 if a is larger than b, 0 if a equals b.
     */
    static compare(a, b) {
        if (a.length < b.length) return -1;
        if (a.length > b.length) return 1;
        for (let i = 0; i < a.length; i++) {
            if (a[i] < b[i]) return -1;
            if (a[i] > b[i]) return 1;
        }
        return 0;
    }
}
BufferUtils.BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
BufferUtils._BASE64_LOOKUP = [];
for (let i = 0, len = BufferUtils.BASE64_ALPHABET.length; i < len; ++i) {
    BufferUtils._BASE64_LOOKUP[i] = BufferUtils.BASE64_ALPHABET[i];
}
BufferUtils.BASE64_LEX_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~';
BufferUtils.BASE64_TO_BASE64_LEX = { '=': '-' };
BufferUtils.BASE64_LEX_TO_BASE64 = { '-': '=' };
for (let i = 0, len = BufferUtils.BASE64_ALPHABET.length; i < len; ++i) {
    BufferUtils.BASE64_TO_BASE64_LEX[BufferUtils.BASE64_ALPHABET[i]] = BufferUtils.BASE64_LEX_ALPHABET[i];
    BufferUtils.BASE64_LEX_TO_BASE64[BufferUtils.BASE64_LEX_ALPHABET[i]] = BufferUtils.BASE64_ALPHABET[i];
}
Class.register(BufferUtils);