// Micro BERT encoder/decoder // Copyright (c) Maxim Sokhatsky (@5HT) function atom(o) { return { type: "Atom", value: o, toString: function() { return this.value; } }; }; function bin(o) { return { type: "Binary", value: o, toString: function() { return "<<\'"+this.value+"'>>"; } }; }; function tuple() { return { type: "Tuple", value: arguments, toString: function() { var s = ""; for (var i=0;i 0) { throw ("BERT: Range: " + OriginalInt); } return s; }; function ltoi(S, Length) { var isNegative, i, n, Num = 0; isNegative = (S.charCodeAt(0) > 128); for (i = 0; i < Length; i++) { n = S.charCodeAt(i); if (isNegative) { n = 255 - n; } if (Num === 0) { Num = n; } else { Num = Num * 256 + n; } } if (isNegative) { Num = Num * (0 - 1); } return Num; }; function btol(Int) { var isNegative, Rem, s = ""; isNegative = Int < 0; if (isNegative) { Int *= -1; s += itoa(1); } else { s += itoa(0); } while (Int !== 0) { Rem = Int % 256; s += itoa(Rem); Int = Math.floor(Int / 256); } return s; }; function ltob(S, Count) { var isNegative, i, n, Num = 0; isNegative = (S.charCodeAt(0) === 1); S = S.substring(1); for (i = Count - 1; i >= 0; i--) { n = S.charCodeAt(i); if (Num === 0) { Num = n; } else { Num = Num * 256 + n; } } if (isNegative) { return Num * -1; } return Num; }; function encode(o) { return BERT + en_inner(o); }; function en_inner(Obj) { if(Obj === undefined) return NIL; var func = 'en_' + typeof(Obj); return eval(func)(Obj); }; function en_string(Obj) { return STR + itol(Obj.length, 2) + Obj; }; function en_boolean(Obj) { if (Obj) return en_inner(atom("true")); else return en_inner(atom("false")); }; function en_number(Obj) { var s, isi = (Obj % 1 === 0); if (!isi) { return en_float(Obj); } if (isi && Obj >= 0 && Obj < 256) { return SINT + itol(Obj, 1); } return INT + itol(Obj, 4); }; function en_float(Obj) { var s = Obj.toExponential(); while (s.length < 31) { s += ZERO; } return FLOAT + s; }; function en_object(Obj) { if (Obj.type === "Atom") return en_atom(Obj); if (Obj.type === "Binary") return en_bin(Obj); if (Obj.type === "Tuple") return en_tuple(Obj); if (Obj.constructor.toString().indexOf("Array") !== -1) return en_array(Obj); return en_associative_array(Obj); }; function en_atom(Obj) { return ATOM + itol(Obj.value.length, 2) + Obj.value; }; function en_bin(Obj) { return BINARY + itol(Obj.value.length, 4) + Obj.value; }; function en_tuple(Obj) { var i, s = ""; if (Obj.value.length < 256) { s += TUPLE + itol(Obj.value.length, 1); } else { s += LTUPLE + itol(Obj.value.length, 4); } for (i = 0; i < Obj.value.length; i++) { s += en_inner(Obj.value[i]); } return s; }; function en_array(Obj) { var i, s = LIST + itol(Obj.length, 4); for (i = 0; i < Obj.length; i++) { s += en_inner(Obj[i]); } s += NIL; return s; }; function en_associative_array(Obj) { var key, Arr = []; for (key in Obj) { if (Obj.hasOwnProperty(key)) { Arr.push(tuple(atom(key), Obj[key])); } } return en_array(Arr); }; function decode(S) { if (S[0] !== BERT) { throw ("Not a valid BERT."); } var Obj = de_inner(S.substring(1)); if (Obj.rest !== "") { throw ("Invalid BERT."); } return Obj.value; }; function de_inner(S) { var Type = S[0]; S = S.substring(1); switch (Type) { case SATOM: de_atom(S, 1); case ATOM: return de_atom(S, 2); case BINARY: return de_bin(S); case SINT: return de_integer(S, 1); case INT: return de_integer(S, 4); case SBIG: return de_big(S, 1); case LBIG: return de_big(S, 4); case FLOAT: return de_float(S); case STR: return de_string(S); case LIST: return de_list(S); case TUPLE: return de_tuple(S, 1); case NIL: return de_nil(S); default: throw ("BERT: " + S.charCodeAt(0) + " DECODE string: "+S); } }; function de_atom(S, Count) { var Size, Value; Size = ltoi(S, Count); S = S.substring(Count); Value = S.substring(0, Size); if (Value === "true") { Value = true; } else if (Value === "false") { Value = false; } return { value: atom(Value), rest: S.substring(Size) }; }; function de_bin(S) { var Size = ltoi(S, 4); S = S.substring(4); return { value: bin(S.substring(0, Size)), rest: S.substring(Size) }; }; function de_integer(S, Count) { var Value = ltoi(S, Count); S = S.substring(Count); return { value: Value, rest: S }; }; function de_float(S) { var Size = 31; return { value: parseFloat(S.substring(0, Size)), rest: S.substring(Size) }; }; function de_string(S) { var Size = ltoi(S, 2); S = S.substring(2); return { value: S.substring(0, Size), rest: S.substring(Size) }; }; function de_list(S) { var Size, i, El, LastChar, Arr = []; Size = ltoi(S, 4); S = S.substring(4); for (i = 0; i < Size; i++) { El = de_inner(S); Arr.push(El.value); S = El.rest; } LastChar = S[0]; if (LastChar !== NIL) { throw ("BERT: Wrong NIL."); } S = S.substring(1); return { value: Arr, rest: S }; }; function de_tuple(S, Count) { var Size, i, El, Arr = []; Size = ltoi(S, Count); S = S.substring(Count); for (i = 0; i < Size; i++) { El = de_inner(S); Arr.push(El.value); S = El.rest; } return { value: tuple(Arr), rest: S }; }; function de_nil(S) { return { value: [], rest: S }; }; function de_big(S, Count) { var Size, Value; Size = ltoi(S, Count); S = S.substring(Count); Value = ltob(S, Size); return { value : Value, rest: S.substring(Size + 1) }; }; function utf8toByteArray(str) { var byteArray = []; if (str !== undefined && str !== null) for (var i = 0; i < str.length; i++) if (str.charCodeAt(i) <= 0x7F) byteArray.push(str.charCodeAt(i)); else { var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); for (var j = 0; j < h.length; j++) byteArray.push(parseInt(h[j], 16)); } return byteArray; } function utf8decode(utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while ( i < utftext.length ) { c = utftext.charCodeAt(i); if (c < 128) {string += String.fromCharCode(c); i++;} else if((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i+1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; }