UNPKG

mongo-portable

Version:

Portable Pure JS MongoDB - Based on Monglodb (https://github.com/euforic/monglodb.git) by Christian Sullivan (http://RogueSynaptics.com)

677 lines (550 loc) 21.6 kB
/* tslint:disable:no-bitwise */ import { JSWLogger } from "jsw-logger"; import * as _ from "lodash"; import { BinaryParserBuffer } from "./BinaryParserBuffer"; // Shorcut for String.fromCharCode const chr = String.fromCharCode; const maxBits = []; for (let i = 0; i < 64; i++) { maxBits[i] = Math.pow(2, i); } export class BinaryParser { protected logger: JSWLogger; private bigEndian: boolean; private allowExceptions: boolean; constructor(bigEndian: boolean = false, allowExceptions: boolean = true) { this.logger = JSWLogger.instance; this.bigEndian = bigEndian; this.allowExceptions = allowExceptions; } /*** * Generate a 12 byte id string used in ObjectId"s * * @method BinaryParser#generate12string * * @return {String} The 12 byte id binary string. */ public generate12string(): string { const time9bytes = Date.now().toString(32); const rnd3bytes = this.encodeInt(parseInt((Math.random() * 0xFFFFFF).toString(), 10), 24, false); return time9bytes + rnd3bytes; } public decodeFloat(data: string | number, precisionBits: number, exponentBits: number): number { const b = new BinaryParserBuffer(this.bigEndian, data); b.checkBuffer(precisionBits + exponentBits + 1); const bias = maxBits[exponentBits - 1] - 1; const signal = b.readBits(precisionBits + exponentBits, 1); const exponent = b.readBits(precisionBits, exponentBits); let significand = 0; let divisor = 2; let curByte = b.buffer.length + (-precisionBits >> 3) - 1; let byteValue; let startBit; let mask; do { // tslint:disable-next-line:semicolon no-conditional-assignment for (byteValue = b.buffer[++curByte], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; (byteValue & mask) && (significand += 1 / divisor), divisor *= 2) { ; } // tslint:disable-next-line:no-conditional-assignment } while (precisionBits -= startBit); return exponent === (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand : Math.pow(2, exponent - bias) * (1 + significand) : 0); /*if (exponent == (bias << 1) + 1) { if (significand) { return NaN; } else { if (signal) { return -Infinity; } else { return +Infinity; } } } else { let part1 = ( 1 + signal * -2 ); if (exponent || significand) { if (!exponent) { return part1 * (Math.pow( 2, -bias + 1 ) * significand); } else { return part1 * (Math.pow( 2, exponent - bias ) * ( 1 + significand )); } } else { // return return part1 * (0); return 0; } }*/ } /*decodeFloat_(data: string|number, precisionBits: number, exponentBits: number): number { var b = new BinaryParserBuffer(this.bigEndian, data); b.checkBuffer(precisionBits + exponentBits + 1); var bias = maxBits[exponentBits - 1] - 1, signal = b.readBits(precisionBits + exponentBits, 1), exponent = b.readBits(precisionBits, exponentBits), significand = 0, divisor = 2, curByte = b.buffer.length + (-precisionBits >> 3) - 1; curByte++; let byteValue, startBit, mask; do { // for ( // var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; // mask >>= 1; // ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 // ); byteValue = b.buffer[curByte]; startBit = precisionBits % 8 || 8; mask = 1 << startBit; mask >>= 1; while (mask) { (byteValue & mask) && (significand += 1 / divisor); divisor *= 2; mask >>= 1 } precisionBits -= startBit } while (precisionBits); // return exponent == ( bias << 1 ) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : ( 1 + signal * -2 ) * ( exponent || significand ? !exponent ? Math.pow( 2, -bias + 1 ) * significand : Math.pow( 2, exponent - bias ) * ( 1 + significand ) : 0 ); if ( exponent == ( bias << 1 ) + 1 ) { if (significand) { return NaN; } else { if (signal) { return -Infinity; } else { return +Infinity; } } } else { var _mod = 0; if (exponent || significand) { _mod = exponent ? Math.pow( 2, exponent - bias ) * ( 1 + significand ) : Math.pow( 2, -bias + 1 ) * significand; } return ( 1 + signal * -2 ) * (_mod); } }*/ public decodeInt(data: string | number, bits: number, signed: boolean, forceBigEndian?: boolean): number { const b = new BinaryParserBuffer(this.bigEndian || forceBigEndian, data); const x = b.readBits(0, bits); const max = maxBits[bits]; // max = Math.pow( 2, bits ); return signed && x >= max / 2 ? x - max : x; } /*decodeInt_(data: string|number, bits: number, signed: boolean, forceBigEndian?: boolean): number { var b = new BinaryParserBuffer(this.bigEndian || forceBigEndian, data), x = b.readBits(0, bits), max = maxBits[bits]; //max = Math.pow( 2, bits ); return signed && x >= max / 2 ? x - max : x; }*/ public encodeFloat(data: /*string|*/number, precisionBits: number, exponentBits: number): string { const bias = maxBits[exponentBits - 1] - 1; const minExp = -bias + 1; const maxExp = bias; const minUnnormExp = minExp - precisionBits; // let n = parseFloat(_.toString(data)); let n = data; const status = isNaN(n) || n === -Infinity || n === +Infinity ? n : 0; let exp = 0; const len = 2 * bias + 1 + precisionBits + 3; const bin = new Array(len); let signal = (n = status !== 0 ? 0 : n) < 0; let intPart = Math.floor(n = Math.abs(n)); let floatPart = n - intPart; let lastBit; let rounded; let result; let i; let j; // tslint:disable-next-line:semicolon for (i = len; i; bin[--i] = 0) { ; } // tslint:disable-next-line:semicolon for (i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2)) { ; } // for (i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0 ) && --floatPart); // tslint:disable-next-line:semicolon for (i = bias + 1; floatPart > 0 && i; (bin[++i] = _.toNumber(((floatPart *= 2) >= 1))) && --floatPart) { ; } // tslint:disable-next-line:semicolon for (i = -1; ++i < len && !bin[i];) { ; } // tslint:disable-next-line:no-conditional-assignment if (bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]) { // tslint:disable-next-line:no-conditional-assignment if (!(rounded = bin[lastBit])) { // tslint:disable-next-line:semicolon for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]) { ; } } // for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0)); // tslint:disable-next-line:semicolon for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = _.toNumber(!bin[j])) && (rounded = 0)) { ; } // j = lastBit + 1; // while (rounded && --j >= 0) { // bin[j] = _.toNumber(!bin[j]); // rounded = 0; // } } // tslint:disable-next-line:semicolon for (i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];) { ; } // tslint:disable-next-line:no-conditional-assignment if ((exp = bias + 1 - i) >= minExp && exp <= maxExp) { ++i; } else if (exp < minExp) { if (exp !== bias + 1 - len && exp < minUnnormExp) { this.logger.warn("encodeFloat::float underflow"); } i = bias + 1 - (exp = minExp - 1); } if (intPart || status !== 0) { this.logger.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status); exp = maxExp + 1; i = bias + 2; if (status === -Infinity) { signal = true; } else if (isNaN(status)) { bin[i] = 1; } } // tslint:disable-next-line:semicolon for (n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1) { ; } let r = []; for (n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = []; i; j = (j + 1) % 8) { n += (1 << j) * result.charAt(--i); if (j === 7) { r[r.length] = String.fromCharCode(n); n = 0; } } r[r.length] = n ? String.fromCharCode(n) : ""; return (this.bigEndian ? r.reverse() : r).join(""); } /* encodeFloat_(data: string|number, precisionBits: number, exponentBits: number): string { var bias = maxBits[exponentBits - 1] - 1, minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits, n = parseFloat(`${data}`), status = isNaN(n) || n == -Infinity || n == +Infinity ? n : 0, exp = 0, len = 2 * bias + 1 + precisionBits + 3, bin: Array<number> = new Array(len), signal: boolean = (n = status !== 0 ? 0 : n) < 0, intPart = Math.floor(n = Math.abs(n)), floatPart: number = n - intPart, lastBit, rounded, result, i, j; // for (i = len; i; bin[--i] = 0); i = len; while (i) { bin[--i] = 0; } // for (i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2)); i = bias + 2; while (intPart && i) { bin[--i] = intPart % 2; intPart = Math.floor(intPart / 2); } // for (i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0 ) && --floatPart); i = bias + 1; while (floatPart > 0 && i) { floatPart *= 2; bin[++i] = floatPart >= 1 ? 1 : 0; --floatPart; } // for (i = -1; ++i < len && !bin[i];); i = 0; while (i < len && !bin[i]) { i++; } if (bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]) { if (!(rounded = bin[lastBit])) { // for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]); j = lastBit + 2; while (!rounded && j < len) { rounded = bin[j++]; } } // for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0)); j = lastBit; while (rounded && j >= 0) { j--; bin[j] = bin[j] ? 0 : 1; rounded = 0; } } for (i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];); if ((exp = bias + 1 - i) >= minExp && exp <= maxExp) { ++i; } else if (exp < minExp) { exp != bias + 1 - len && exp < minUnnormExp && this.logger.warn("encodeFloat::float underflow"); i = bias + 1 - (exp = minExp - 1); } if (intPart || status !== 0) { this.logger.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status); exp = maxExp + 1; i = bias + 2; if (status == -Infinity) { signal = true; // 1 } else if (isNaN(status)) { bin[i] = 1; } } // for (n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1); n = Math.abs(exp + bias); j = exponentBits; result = ""; while (j) { j--; result = (n % 2) + result; n = n >>= 1; } let r = []; // for (n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = []; i; j = (j + 1) % 8) { n = 0; j = 0; result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join(""); i = result.length; r = []; while (i) { j = (j + 1) % 8 n += (1 << j) * result.charAt(--i); if (j == 7) { r[r.length] = String.fromCharCode(n); n = 0; } } r[r.length] = n ? String.fromCharCode(n) : ""; return (this.bigEndian ? r.reverse() : r).join(""); } */ public encodeInt(data: number, bits: number, signed: boolean, forceBigEndian?: boolean): string { const max = maxBits[bits]; let r; if (data >= max || data < -(max / 2)) { this.logger.warn("encodeInt::overflow"); data = 0; } if (data < 0) { data += max; } // tslint:disable-next-line:semicolon for (r = []; data; r[r.length] = String.fromCharCode(data % 256), data = Math.floor(data / 256)) { ; } // tslint:disable-next-line:semicolon for (bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0") { ; } return ((this.bigEndian || forceBigEndian) ? r.reverse() : r).join(""); } /*encodeInt_(data: number, bits: number, signed: boolean, forceBigEndian?: boolean): string { var max = maxBits[bits]; data = data - 0; // Ensure a number if (data >= max || data < -(max / 2)) { this.logger.warn("encodeInt::overflow"); data = 0; } if (data < 0) { data += max; } // for (var r = []; data; r[r.length] = String.fromCharCode(data % 256), data = Math.floor(data / 256)); let r = []; while(data) { r[r.length] = String.fromCharCode(data % 256); data = Math.floor(data / 256); } // for (bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0"); bits = -(-bits >> 3) - r.length; while(bits) { bits--; r[r.length] = "\0"; } return ((this.bigEndian || forceBigEndian) ? r.reverse() : r).join(""); }*/ public toSmall(data: number | string): number { return this.decodeInt(data, 8, true); } public fromSmall(data: number): string { return this.encodeInt(data, 8, true); } public toByte(data: number | string): number { return this.decodeInt(data, 8, false); } public fromByte(data: number): string { return this.encodeInt(data, 8, false); } public toShort(data: number | string): number { return this.decodeInt(data, 16, true); } public fromShort(data: number): string { return this.encodeInt(data, 16, true); } public toWord(data: number | string): number { return this.decodeInt(data, 16, false); } public fromWord(data: number): string { return this.encodeInt(data, 16, false); } public toInt(data: number | string): number { return this.decodeInt(data, 32, true); } public fromInt(data: number): string { return this.encodeInt(data, 32, true); } public toLong(data: number | string): number { return this.decodeInt(data, 64, true); } public fromLong(data: number): string { return this.encodeInt(data, 64, true); } public toDWord(data: number | string): number { return this.decodeInt(data, 32, false); } public fromDWord(data: number): string { return this.encodeInt(data, 32, false); } public toQWord(data: number | string): number { return this.decodeInt(data, 64, true); } public fromQWord(data: number): string { return this.encodeInt(data, 64, true); } public toFloat(data: number | string): number { return this.decodeFloat(data, 23, 8); } public fromFloat(data: number): string { return this.encodeFloat(data, 23, 8); } public toDouble(data: number | string): number { return this.decodeFloat(data, 52, 11); } public fromDouble(data: number): string { return this.encodeFloat(data, 52, 11); } // Factor out the encode so it can be shared by add_header and push_int32 public encode_int32(num: number, asArray: boolean = false): string[] | string { let a; let b; let c; let d; let unsigned; unsigned = (num < 0) ? (num + 0x100000000) : num; a = Math.floor(unsigned / 0xffffff); unsigned &= 0xffffff; b = Math.floor(unsigned / 0xffff); unsigned &= 0xffff; c = Math.floor(unsigned / 0xff); unsigned &= 0xff; d = Math.floor(unsigned); return asArray ? [chr(a), chr(b), chr(c), chr(d)] : chr(a) + chr(b) + chr(c) + chr(d); } public encode_int64(num: number): string { let a; let b; let c; let d; let e; let f; let g; let h; let unsigned; unsigned = (num < 0) ? (num + 0x10000000000000000) : num; a = Math.floor(unsigned / 0xffffffffffffff); unsigned &= 0xffffffffffffff; b = Math.floor(unsigned / 0xffffffffffff); unsigned &= 0xffffffffffff; c = Math.floor(unsigned / 0xffffffffff); unsigned &= 0xffffffffff; d = Math.floor(unsigned / 0xffffffff); unsigned &= 0xffffffff; e = Math.floor(unsigned / 0xffffff); unsigned &= 0xffffff; f = Math.floor(unsigned / 0xffff); unsigned &= 0xffff; g = Math.floor(unsigned / 0xff); unsigned &= 0xff; h = Math.floor(unsigned); return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h); } /*** * UTF8 methods */ // Take a raw binary string and return a utf8 string public decode_utf8(binaryStr: string): string { // let len = binaryStr.length; let decoded = ""; let i = 0; let c = 0; // let c1 = 0; let c2 = 0; let c3; while (i < binaryStr.length) { c = binaryStr.charCodeAt(i); if (c < 128) { decoded += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = binaryStr.charCodeAt(i + 1); decoded += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = binaryStr.charCodeAt(i + 1); c3 = binaryStr.charCodeAt(i + 2); decoded += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return decoded; } // Encode a cstring public encode_cstring(s: string | number): string { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/escape return encodeURIComponent(encodeURIComponent(`${s}`)) + this.fromByte(0); } // Take a utf8 string and return a binary string public encode_utf8(s: string): string { let a = ""; let c; for (let n = 0, len = s.length; n < len; n++) { c = s.charCodeAt(n); if (c < 128) { a += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { a += String.fromCharCode((c >> 6) | 192); a += String.fromCharCode((c & 63) | 128); } else { a += String.fromCharCode((c >> 12) | 224); a += String.fromCharCode(((c >> 6) & 63) | 128); a += String.fromCharCode((c & 63) | 128); } } return a; } public hprint(s: string): number { let num; for (let i = 0, len = s.length; i < len; i++) { if (s.charCodeAt(i) < 32) { num = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16); this.logger.silly(num + " "); } else { num = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16); this.logger.silly(num + " "); } } this.logger.silly("\n\n"); return num; } public ilprint(s: string): number { let num; for (let i = 0, len = s.length; i < len; i++) { if (s.charCodeAt(i) < 32) { num = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(10) : s.charCodeAt(i).toString(10); this.logger.silly(num + " "); } else { num = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(10) : s.charCodeAt(i).toString(10); this.logger.silly(num + " "); } } this.logger.silly("\n\n"); return num; } public hlprint(s: string): number { let num; for (let i = 0, len = s.length; i < len; i++) { if (s.charCodeAt(i) < 32) { num = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16); this.logger.silly(num + " "); } else { num = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16); this.logger.silly(num + " "); } } this.logger.silly("\n\n"); return num; } // Static access to methods public static toSmall(data: number | string): number { return (new BinaryParser()).toSmall(data); } public static fromSmall(data: number): string { return (new BinaryParser()).fromSmall(data); } public static toByte(data: number | string): number { return (new BinaryParser()).toByte(data); } public static fromByte(data: number): string { return (new BinaryParser()).fromByte(data); } public static toShort(data: number | string): number { return (new BinaryParser()).toShort(data); } public static fromShort(data: number): string { return (new BinaryParser()).fromShort(data); } public static toWord(data: number | string): number { return (new BinaryParser()).toWord(data); } public static fromWord(data: number): string { return (new BinaryParser()).fromWord(data); } public static toInt(data: number | string): number { return (new BinaryParser()).toInt(data); } public static fromInt(data: number): string { return (new BinaryParser()).fromInt(data); } public static toLong(data: number | string): number { return (new BinaryParser()).toLong(data); } public static fromLong(data: number): string { return (new BinaryParser()).fromLong(data); } public static toDWord(data: number | string): number { return (new BinaryParser()).toDWord(data); } public static fromDWord(data: number): string { return (new BinaryParser()).fromDWord(data); } public static toQWord(data: number | string): number { return (new BinaryParser()).toQWord(data); } public static fromQWord(data: number): string { return (new BinaryParser()).fromQWord(data); } public static toFloat(data: number | string): number { return (new BinaryParser()).toFloat(data); } public static fromFloat(data: number): string { return (new BinaryParser()).fromFloat(data); } public static toDouble(data: number | string): number { return (new BinaryParser()).toDouble(data); } public static fromDouble(data: number): string { return (new BinaryParser()).fromDouble(data); } public static encode_int32(num: number, asArray: boolean = false): string[] | string { return (new BinaryParser()).encode_int32(num, asArray); } public static encode_int64(num: number): string { return (new BinaryParser()).encode_int64(num); } public static decode_utf8(binaryStr: string): string { return (new BinaryParser()).decode_utf8(binaryStr); } public static encode_cstring(s: string | number): string { return (new BinaryParser()).encode_cstring(s); } public static encode_utf8(s: string): string { return (new BinaryParser()).encode_utf8(s); } public static hprint(s: string): number { return (new BinaryParser()).hprint(s); } public static ilprint(s: string): number { return (new BinaryParser()).ilprint(s); } public static hlprint(s: string): number { return (new BinaryParser()).hlprint(s); } }