├── .gitignore
├── MIT-LICENSE
├── README.markdown
├── bert.js
├── package.json
├── test.html
└── tests.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2009 Rusty Klophaus (@rklophaus)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 |
BERT-JS
2 |
3 | What is BERT?
4 | BERT (Binary ERlang Term) is a format created by the Erlang development team for serializing Erlang terms, and promoted by Tom Preston-Werner as a way for different languages to communicate in a simple and efficient manner.
5 |
6 | Read Tom's Slides
7 |
8 |
9 | What is BERT JS?
10 |
11 | BERT-JS is a first cut Javascript implementation of the BERT protocol. In other words, using BERT-JS, you can serialize data into a binary format that can then be de-serialized by Erlang directly into an Erlang term. (Or, by Ruby, as Tom has written a BERT library for Ruby.)
12 |
13 | Limitations
14 |
15 | * Decoding floats is not yet supported.
16 |
17 | Interface
18 |
19 | * Bert.encode(Object) - Encode a Javascript object into BERT, return a String. The object can be a Boolean, Integer, Float, String, Array, Associative Array, or an Atom, Binary, or Tuple. (with the help of Bert.atom(), Bert.binary(), or Bert.tuple(), respectively).
20 | * Bert.decode(String) - Decode a BERT string into a Javascript object. Atoms, Binaries, and Tuples are special objects. See code for structure.
21 | * Bert.atom(String) - Create a Javascript object that will be encoded to an Atom.
22 | * Bert.binary(String) - Create a Javascript object that will be encoded to an Binary.
23 | * Bert.tuple(Element1, Element2, Element3, ...) - Create a Javascript object that will be encoded to a Tuple.
24 | Examples
25 |
26 | Note, below the return value is given in the form of an Erlang binary which can be fed into Erlang's binary_to_term/1. In reality, this returns a Javascript String with the ASCII values of the binary.
27 |
28 | Bert.encode(Bert.atom("hello"));
29 | Returns: <<131,100,0,5,104,101,108,108,111>>
30 | Erlang: hello
31 |
32 | Bert.encode(Bert.binary("hello"));
33 | Returns: <<131,109,0,0,0,5,104,101,108,108,111>>
34 | Erlang: <<"hello">>
35 |
36 | Bert.encode(true);
37 | Returns: <<131,100,0,4,116,114,117,101>>
38 | Erlang: true
39 |
40 | Bert.encode(42);
41 | Returns: <<131,97,42>>
42 | Erlang: 42
43 |
44 | Bert.encode(5000);
45 | Returns: <<131,98,0,0,19,136>>
46 | Erlang: 5000
47 |
48 | Bert.encode(-5000);
49 | Returns: <<131,98,255,255,236,120>>
50 | Erlang: -5000
51 |
52 | Bert.encode(987654321);
53 | Returns: <<131,110,4,0,177,104,222,58>>
54 | Erlang: 987654321
55 |
56 | Bert.encode(-987654321);
57 | Returns: <<131,110,4,1,177,104,222,58>>
58 | Erlang: -987654321
59 |
60 | Bert.encode(3.14159);
61 | Returns: <<131,99,51,46,49,52,49,53,57,101,43,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>
62 | Erlang: 3.14159
63 |
64 | Bert.encode(-3.14159);
65 | Returns: <<131,99,45,51,46,49,52,49,53,57,101,43,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>
66 | Erlang: -3.14159
67 |
68 | Bert.encode([1, 2, 3]);
69 | Returns: <<131,108,0,0,0,3,97,1,97,2,97,3,106>>
70 | Erlang: [1,2,3]
71 |
72 | Bert.encode({a:1, b:2, c:3});
73 | Returns: <<131,108,0,0,0,3,104,2,100,0,1,97,97,1,104,2,100,0,1,98,97,2,104,2,100,0,1,99,97,3,106>>
74 | Erlang: [{a,1},{b,2},{c,3}]
75 |
76 | Bert.encode(Bert.tuple("Hello", 1));
77 | Returns: <<131,104,2,107,0,5,72,101,108,108,111,97,1>>
78 | Erlang: {"Hello",1}
79 |
80 | Bert.encode({
81 | a : Bert.tuple(1, 2, 3),
82 | b : [4,5,6]
83 | });
84 | Returns: <<131,108,0,0,0,2,104,2,100,0,1,97,104,3,97,1,97,2,97,3,104,2,100,0,1,98,108,0,0,0,3,97,4,97,5,97,6,106,106>>
85 | Erlang: [{a,{1,2,3}},{b,[4,5,6]}]
86 |
87 | var S = Bert.bytes_to_string([131,108,0,0,0,3,104,2,100,0,4,97,116,111,109,100,0,6,109,121,65,116,111,109,
88 | 104,2,100,0,6,98,105,110,97,114,121,109,0,0,0,9,77,121,32,66,105,110,97,114,
89 | 121,104,2,100,0,4,98,111,111,108,100,0,4,116,114,117,101,106]);
90 | var Obj = Bert.decode(S);
91 | Object is equiv to: [{atom,myAtom},{binary,<<"My Binary">>},{bool,true},{string,"Hello there"}]
92 |
93 | var S = Bert.bytes_to_string([131,108,0,0,0,5,104,2,100,0,13,115,109,97,108,108,95,105,110,116,101,103,101,
94 | 114,97,42,104,2,100,0,8,105,110,116,101,103,101,114,49,98,0,0,19,136,104,2, 100,0,8,105,110,116,101,103,
95 | 101,114,50,98,255,255,236,120,104,2,100,0,8,98,105,103,95,105,110,116,49,110,4,0,177,104,222,58,104,2,
96 | 100,0,8,98,105,103,95,105,110,116,50,110,4,1,177,104,222,58,106]);
97 | var Obj = Bert.decode(S);
98 | Object is equiv to: [{small_integer,42},{integer1,5000},{integer2,-5000},{big_int1,987654321},{big_int2,-987654321}]
--------------------------------------------------------------------------------
/bert.js:
--------------------------------------------------------------------------------
1 | // BERT-JS
2 | // Copyright (c) 2009 Rusty Klophaus (@rklophaus)
3 | // Contributions by Ben Browning (@bbrowning)
4 | // See MIT-LICENSE for licensing information.
5 |
6 |
7 | // BERT-JS is a Javascript implementation of Binary Erlang Term Serialization.
8 | // - http://github.com/rklophaus/BERT-JS
9 | //
10 | // References:
11 | // - http://www.erlang-factory.com/upload/presentations/36/tom_preston_werner_erlectricity.pdf
12 | // - http://www.erlang.org/doc/apps/erts/erl_ext_dist.html#8
13 |
14 |
15 | // - CLASSES -
16 |
17 | function BertClass() {
18 | this.BERT_START = String.fromCharCode(131);
19 | this.SMALL_ATOM = String.fromCharCode(115);
20 | this.ATOM = String.fromCharCode(100);
21 | this.BINARY = String.fromCharCode(109);
22 | this.SMALL_INTEGER = String.fromCharCode(97);
23 | this.INTEGER = String.fromCharCode(98);
24 | this.SMALL_BIG = String.fromCharCode(110);
25 | this.LARGE_BIG = String.fromCharCode(111);
26 | this.FLOAT = String.fromCharCode(99);
27 | this.STRING = String.fromCharCode(107);
28 | this.LIST = String.fromCharCode(108);
29 | this.SMALL_TUPLE = String.fromCharCode(104);
30 | this.LARGE_TUPLE = String.fromCharCode(105);
31 | this.NIL = String.fromCharCode(106);
32 | this.ZERO = String.fromCharCode(0);
33 | this.ZERO_CHAR = String.fromCharCode(48);
34 | }
35 |
36 | function BertAtom(Obj) {
37 | this.type = "Atom";
38 | this.value = Obj;
39 | this.toString = function () {
40 | return Obj;
41 | };
42 | }
43 |
44 | function BertBinary(Obj) {
45 | this.type = "Binary";
46 | this.value = Obj;
47 | this.toString = function () {
48 | return "<<\"" + Obj + "\">>";
49 | };
50 | }
51 |
52 | function BertTuple(Arr) {
53 | this.type = "Tuple";
54 | this.length = Arr.length;
55 | this.value = Arr;
56 | for (var i = 0; i < Arr.length; i++) {
57 | this[i] = Arr[i];
58 | }
59 | this.toString = function () {
60 | var i, s = "";
61 | for (i = 0; i < this.length; i++) {
62 | if (s !== "") {
63 | s += ", ";
64 | }
65 | s += this[i].toString();
66 | }
67 |
68 | return "{" + s + "}";
69 | };
70 | }
71 |
72 |
73 |
74 | // - INTERFACE -
75 |
76 | BertClass.prototype.encode = function (Obj) {
77 | return this.BERT_START + this.encode_inner(Obj);
78 | };
79 |
80 | BertClass.prototype.decode = function (S) {
81 | if (S[0] !== this.BERT_START) {
82 | throw ("Not a valid BERT.");
83 | }
84 | var Obj = this.decode_inner(S.substring(1));
85 | if (Obj.rest !== "") {
86 | throw ("Invalid BERT.");
87 | }
88 | return Obj.value;
89 | };
90 |
91 | BertClass.prototype.atom = function (Obj) {
92 | return new BertAtom(Obj);
93 | };
94 |
95 | BertClass.prototype.binary = function (Obj) {
96 | return new BertBinary(Obj);
97 | };
98 |
99 | BertClass.prototype.tuple = function () {
100 | return new BertTuple(arguments);
101 | };
102 |
103 |
104 |
105 | // - ENCODING -
106 |
107 | BertClass.prototype.encode_inner = function (Obj) {
108 | if (Obj === undefined) throw new Error("Cannot encode undefined values.")
109 | var func = 'encode_' + typeof(Obj);
110 | return this[func](Obj);
111 | };
112 |
113 | BertClass.prototype.encode_string = function (Obj) {
114 | return this.STRING + this.int_to_bytes(Obj.length, 2) + Obj;
115 | };
116 |
117 | BertClass.prototype.encode_boolean = function (Obj) {
118 | if (Obj) {
119 | return this.encode_inner(
120 | this.tuple(this.atom("bert"), this.atom("true")));
121 | }
122 | else {
123 | return this.encode_inner(
124 | this.tuple(this.atom("bert"), this.atom("false")));
125 | }
126 | };
127 |
128 | BertClass.prototype.encode_number = function (Obj) {
129 | var s, isInteger = (Obj % 1 === 0);
130 |
131 | // Handle floats...
132 | if (!isInteger) {
133 | return this.encode_float(Obj);
134 | }
135 |
136 | // Small int...
137 | if (isInteger && Obj >= 0 && Obj < 256) {
138 | return this.SMALL_INTEGER + this.int_to_bytes(Obj, 1);
139 | }
140 |
141 | // 4 byte int...
142 | if (isInteger && Obj >= -134217728 && Obj <= 134217727) {
143 | return this.INTEGER + this.int_to_bytes(Obj, 4);
144 | }
145 |
146 | // Bignum...
147 | s = this.bignum_to_bytes(Obj);
148 | if (s.length < 256) {
149 | return this.SMALL_BIG + this.int_to_bytes(s.length - 1, 1) + s;
150 | } else {
151 | return this.LARGE_BIG + this.int_to_bytes(s.length - 1, 4) + s;
152 | }
153 | };
154 |
155 | BertClass.prototype.encode_float = function (Obj) {
156 | // float...
157 | var s = Obj.toExponential(20);
158 | var match = /([^e]+)(e[+-])(\d+)/.exec(s);
159 | var a = match[1];
160 | var b = match[2];
161 | var c = match[3];
162 | var exponentialPart = c;
163 | if ( exponentialPart.length == 1 ) {
164 | exponentialPart = '0' + exponentialPart;
165 | }
166 | s = a+b+exponentialPart;
167 | while (s.length < 31) {
168 | s += this.ZERO;
169 | }
170 | return this.FLOAT + s;
171 | };
172 |
173 | BertClass.prototype.encode_object = function (Obj) {
174 | // Check if it's an atom, binary, or tuple...
175 | if (Obj === null){
176 | return this.encode_inner(this.atom("null"));
177 | }
178 | if (Obj.type === "Atom") {
179 | return this.encode_atom(Obj);
180 | }
181 | if (Obj.type === "Binary") {
182 | return this.encode_binary(Obj);
183 | }
184 | if (Obj.type === "Tuple") {
185 | return this.encode_tuple(Obj);
186 | }
187 |
188 | // Check if it's an array...
189 | if (Obj.constructor.toString().indexOf("Array") !== -1) {
190 | return this.encode_array(Obj);
191 | }
192 |
193 | // Treat the object as an associative array...
194 | return this.encode_associative_array(Obj);
195 | };
196 |
197 | BertClass.prototype.encode_atom = function (Obj) {
198 | return this.ATOM + this.int_to_bytes(Obj.value.length, 2) + Obj.value;
199 | };
200 |
201 | BertClass.prototype.encode_binary = function (Obj) {
202 | return this.BINARY + this.int_to_bytes(Obj.value.length, 4) + Obj.value;
203 | };
204 |
205 | BertClass.prototype.encode_tuple = function (Obj) {
206 | var i, s = "";
207 | if (Obj.length < 256) {
208 | s += this.SMALL_TUPLE + this.int_to_bytes(Obj.length, 1);
209 | } else {
210 | s += this.LARGE_TUPLE + this.int_to_bytes(Obj.length, 4);
211 | }
212 | for (i = 0; i < Obj.length; i++) {
213 | s += this.encode_inner(Obj[i]);
214 | }
215 | return s;
216 | };
217 |
218 | BertClass.prototype.encode_array = function (Obj) {
219 | if (Obj.length == 0)
220 | return this.encode_inner(
221 | this.tuple(this.atom("bert"), this.atom("nil")));
222 | var i, s = this.LIST + this.int_to_bytes(Obj.length, 4);
223 | for (i = 0; i < Obj.length; i++) {
224 | s += this.encode_inner(Obj[i]);
225 | }
226 | s += this.NIL;
227 | return s;
228 | };
229 |
230 | BertClass.prototype.encode_associative_array = function (Obj) {
231 | var key, Arr = [];
232 | for (key in Obj) {
233 | if (Obj.hasOwnProperty(key)) {
234 | Arr.push(this.tuple(this.atom(key), Obj[key]));
235 | }
236 | }
237 | return this.encode_array(Arr);
238 | };
239 |
240 |
241 |
242 | // - DECODING -
243 |
244 | BertClass.prototype.decode_inner = function (S) {
245 | var Type = S[0];
246 | S = S.substring(1);
247 | switch (Type) {
248 | case this.SMALL_ATOM:
249 | return this.decode_atom(S, 1);
250 | case this.ATOM:
251 | return this.decode_atom(S, 2);
252 | case this.BINARY:
253 | return this.decode_binary(S);
254 | case this.SMALL_INTEGER:
255 | return this.decode_small_integer(S);
256 | case this.INTEGER:
257 | return this.decode_integer(S, 4);
258 | case this.SMALL_BIG:
259 | return this.decode_big(S, 1);
260 | case this.LARGE_BIG:
261 | return this.decode_big(S, 4);
262 | case this.FLOAT:
263 | return this.decode_float(S);
264 | case this.STRING:
265 | return this.decode_string(S);
266 | case this.LIST:
267 | return this.decode_list(S);
268 | case this.SMALL_TUPLE:
269 | return this.decode_tuple(S, 1);
270 | case this.LARGE_TUPLE:
271 | return this.decode_large_tuple(S, 4);
272 | case this.NIL:
273 | return this.decode_nil(S);
274 | default:
275 | throw ("Unexpected BERT type: " + S.charCodeAt(0));
276 | }
277 | };
278 |
279 | BertClass.prototype.decode_atom = function (S, Count) {
280 | var Size, Value;
281 | Size = this.bytes_to_int(S, Count);
282 | S = S.substring(Count);
283 | Value = S.substring(0, Size);
284 | return {
285 | value: this.atom(Value),
286 | rest: S.substring(Size)
287 | };
288 | };
289 |
290 | BertClass.prototype.decode_binary = function (S) {
291 | var Size = this.bytes_to_int(S, 4);
292 | S = S.substring(4);
293 | return {
294 | value: this.binary(S.substring(0, Size)),
295 | rest: S.substring(Size)
296 | };
297 | };
298 |
299 | BertClass.prototype.decode_small_integer = function (S) {
300 | var Value = S.charCodeAt(0);
301 | S = S.substring(1);
302 | return {
303 | value: Value,
304 | rest: S
305 | };
306 | };
307 |
308 | BertClass.prototype.decode_integer = function (S, Count) {
309 | var Value = this.bytes_to_int(S, Count);
310 | S = S.substring(Count);
311 | return {
312 | value: Value,
313 | rest: S
314 | };
315 | };
316 |
317 | BertClass.prototype.decode_big = function (S, Count) {
318 | var Size, Value;
319 | Size = this.bytes_to_int(S, Count);
320 | S = S.substring(Count);
321 | Value = this.bytes_to_bignum(S, Size);
322 | return {
323 | value : Value,
324 | rest: S.substring(Size + 1)
325 | };
326 | };
327 |
328 | BertClass.prototype.decode_float = function (S) {
329 | var Size = 31;
330 | return {
331 | value: parseFloat(S.substring(0, Size)),
332 | rest: S.substring(Size)
333 | };
334 | };
335 |
336 | BertClass.prototype.decode_string = function (S) {
337 | var Size = this.bytes_to_int(S, 2);
338 | S = S.substring(2);
339 | return {
340 | value: S.substring(0, Size),
341 | rest: S.substring(Size)
342 | };
343 | };
344 |
345 | BertClass.prototype.decode_list = function (S) {
346 | var Size, i, El, LastChar, Arr = [];
347 | Size = this.bytes_to_int(S, 4);
348 | S = S.substring(4);
349 | for (i = 0; i < Size; i++) {
350 | El = this.decode_inner(S);
351 | Arr.push(El.value);
352 | S = El.rest;
353 | }
354 | LastChar = S[0];
355 | if (LastChar !== this.NIL) {
356 | throw ("List does not end with NIL!");
357 | }
358 | S = S.substring(1);
359 | return {
360 | value: Arr,
361 | rest: S
362 | };
363 | };
364 |
365 | BertClass.prototype.decode_tuple = function (S, Count) {
366 | var Size, i, El, Arr = [];
367 | Size = this.bytes_to_int(S, Count);
368 | S = S.substring(Count);
369 | for (i = 0; i < Size; i++) {
370 | El = this.decode_inner(S);
371 | Arr.push(El.value);
372 | S = El.rest;
373 | }
374 | if (Size >= 2) {
375 | var Head = Arr[0];
376 | if (typeof Head === 'object' && Head.type === 'Atom'
377 | && Head.value === "bert") {
378 | var Kind = Arr[1];
379 | if (typeof Kind !== 'object' || Kind.type !== 'Atom') {
380 | throw ("Invalid {bert, _} tuple!");
381 | }
382 | switch (Kind.value) {
383 | case "true":
384 | return {value: true, rest: S};
385 | case "false":
386 | return {value: false, rest: S};
387 | case "nil":
388 | return {value: [], rest: S};
389 | case "time":
390 | case "dict":
391 | case "regex":
392 | throw ("TODO: decode " + Kind.Value);
393 | default:
394 | throw ("Invalid {bert, " +
395 | Kind.Value.toString() + "} tuple!");
396 | }
397 | }
398 | }
399 | return {
400 | value: this.tuple.apply(this,Arr),
401 | rest: S
402 | };
403 | };
404 |
405 | BertClass.prototype.decode_nil = function (S) {
406 | // nil is an empty list
407 | return {
408 | value: [],
409 | rest: S
410 | };
411 | };
412 |
413 |
414 |
415 | // - UTILITY FUNCTIONS -
416 |
417 | // Encode an integer to a big-endian byte-string of length Length.
418 | // Throw an exception if the integer is too large
419 | // to fit into the specified number of bytes.
420 | BertClass.prototype.int_to_bytes = function (Int, Length) {
421 | var isNegative, OriginalInt, i, Rem, s = "";
422 | isNegative = (Int < 0);
423 | if (isNegative) {
424 | Int = - Int - 1;
425 | }
426 | OriginalInt = Int;
427 | for (i = 0; i < Length; i++) {
428 | Rem = Int % 256;
429 | if (isNegative) {
430 | Rem = 255 - Rem;
431 | }
432 | s = String.fromCharCode(Rem) + s;
433 | Int = Math.floor(Int / 256);
434 | }
435 | if (Int > 0) {
436 | throw ("Argument out of range: " + OriginalInt);
437 | }
438 | return s;
439 | };
440 |
441 | // Read a big-endian encoded integer from the first Length bytes
442 | // of the supplied string.
443 | BertClass.prototype.bytes_to_int = function (S, Length) {
444 | var isNegative, i, n, Num = 0;
445 | isNegative = (S.charCodeAt(0) > 128);
446 | for (i = 0; i < Length; i++) {
447 | n = S.charCodeAt(i);
448 | if (isNegative) {
449 | n = 255 - n;
450 | }
451 | if (Num === 0) {
452 | Num = n;
453 | }
454 | else {
455 | Num = Num * 256 + n;
456 | }
457 | }
458 | if (isNegative) {
459 | Num = -Num - 1;
460 | }
461 | return Num;
462 | };
463 |
464 | // Encode an integer into an Erlang bignum,
465 | // which is a byte of 1 or 0 representing
466 | // whether the number is negative or positive,
467 | // followed by little-endian bytes.
468 | BertClass.prototype.bignum_to_bytes = function (Int) {
469 | var isNegative, Rem, s = "";
470 | isNegative = Int < 0;
471 | if (isNegative) {
472 | Int *= -1;
473 | s += String.fromCharCode(1);
474 | } else {
475 | s += String.fromCharCode(0);
476 | }
477 |
478 | while (Int !== 0) {
479 | Rem = Int % 256;
480 | s += String.fromCharCode(Rem);
481 | Int = Math.floor(Int / 256);
482 | }
483 |
484 | return s;
485 | };
486 |
487 | // Encode a list of bytes into an Erlang bignum.
488 | BertClass.prototype.bytes_to_bignum = function (S, Count) {
489 | var isNegative, i, n, Num = 0;
490 | isNegative = (S.charCodeAt(0) === 1);
491 | S = S.substring(1);
492 | for (i = Count - 1; i >= 0; i--) {
493 | n = S.charCodeAt(i);
494 | if (Num === 0) {
495 | Num = n;
496 | }
497 | else {
498 | Num = Num * 256 + n;
499 | }
500 | }
501 | if (isNegative) {
502 | return Num * -1;
503 | }
504 | return Num;
505 | };
506 |
507 | // Convert an array of bytes into a string.
508 | BertClass.prototype.bytes_to_string = function (Arr) {
509 | var i, s = "";
510 | for (i = 0; i < Arr.length; i++) {
511 | s += String.fromCharCode(Arr[i]);
512 | }
513 | return s;
514 | };
515 |
516 | // - TESTING -
517 |
518 | // Pretty Print a byte-string in Erlang binary form.
519 | BertClass.prototype.pp_bytes = function (Bin) {
520 | var i, s = "";
521 | for (i = 0; i < Bin.length; i++) {
522 | if (s !== "") {
523 | s += ",";
524 | }
525 | s += "" + Bin.charCodeAt(i);
526 | }
527 | return "<<" + s + ">>";
528 | };
529 |
530 | // Pretty Print a JS object in Erlang term form.
531 | BertClass.prototype.pp_term = function (Obj) {
532 | return Obj.toString();
533 | };
534 |
535 | BertClass.prototype.binary_to_list = function (Str){
536 | var ret = [];
537 | for (var i = 0; i < Str.length; i++)
538 | ret.push(Str.charCodeAt(i));
539 | return ret;
540 | };
541 |
542 | module.exports = new BertClass();
543 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bert-js",
3 | "version": "1.0.0",
4 | "description": "Erlang BERT format encoder/decoder for javascript",
5 | "main": "bert.js",
6 | "scripts": {
7 | "test": "mocha tests.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/stjepano/BERT-JS.git"
12 | },
13 | "author": "",
14 | "license": "ISC",
15 | "bugs": {
16 | "url": "https://github.com/stjepano/BERT-JS/issues"
17 | },
18 | "homepage": "https://github.com/stjepano/BERT-JS#readme",
19 | "dependencies": {
20 | "chai": "^3.5.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests.js:
--------------------------------------------------------------------------------
1 | var Bert = require('./bert.js');
2 | var chai = require('chai');
3 |
4 | var should = chai.should();
5 | var expect = chai.expect;
6 |
7 | describe('Bert', function() {
8 |
9 | it ('should encode atom', function() {
10 | Bert.binary_to_list(Bert.encode(Bert.atom('hello'))).should.deep.equal([
11 | 131,100,0,5,104,101,108,108,111
12 | ]);
13 | });
14 |
15 | it ('should encode binary', function() {
16 | Bert.binary_to_list(Bert.encode(Bert.binary("hello"))).should.deep.equal([
17 | 131,109,0,0,0,5,104,101,108,108,111
18 | ]);
19 | });
20 |
21 | it ('should encode boolean', function() {
22 | Bert.binary_to_list(Bert.encode(true)).should.deep.equal([
23 | 131,104,2,100,0,4,98,101,114,116,100,0,4,116,114,117,101
24 | ]);
25 |
26 | Bert.binary_to_list(Bert.encode(false)).should.deep.equal([
27 | 131,104,2,100,0,4,98,101,114,116,100,0,5,102,97,108,115,101
28 | ]);
29 | });
30 |
31 | it ('should encode ints', function() {
32 | expect(Bert.binary_to_list(Bert.encode(0))).to.deep.equal([
33 | 131,97,0
34 | ]);
35 |
36 | expect(Bert.binary_to_list(Bert.encode(-1))).to.deep.equal([
37 | 131,98,255,255,255,255
38 | ]);
39 |
40 | expect(Bert.binary_to_list(Bert.encode(42))).to.deep.equal([
41 | 131,97,42
42 | ]);
43 |
44 | expect(Bert.binary_to_list(Bert.encode(5000))).to.deep.equal([
45 | 131,98,0,0,19,136
46 | ]);
47 |
48 | expect(Bert.binary_to_list(Bert.encode(-5000))).to.deep.equal([
49 | 131,98,255,255,236,120
50 | ]);
51 |
52 | expect(Bert.binary_to_list(Bert.encode(987654321))).to.deep.equal([
53 | 131,110,4,0,177,104,222,58
54 | ]);
55 |
56 | expect(Bert.binary_to_list(Bert.encode(-987654321))).to.deep.equal([
57 | 131,110,4,1,177,104,222,58
58 | ]);
59 | });
60 |
61 | it ('should encode null', function() {
62 | expect(Bert.binary_to_list(Bert.encode(null))).to.deep.equal([
63 | 131,100,0,4,110,117,108,108
64 | ]);
65 | });
66 |
67 | it ('should not encode undefined', function() {
68 | expect(function(){
69 | Bert.binary_to_list(Bert.encode(undefined))
70 | }).to.throw("Cannot encode undefined values.")
71 | });
72 |
73 | it ('should encode floats', function() {
74 | expect(Bert.binary_to_list(Bert.encode(2.5))).to.deep.equal([
75 | 131,99,50,46,53,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,
76 | 43,48,48,0,0,0,0,0
77 | ], 'failed 2.5');
78 |
79 | expect(Bert.binary_to_list(Bert.encode(3.14159))).to.deep.equal([
80 | 131,99,51,46,49,52,49,53,56,57,57,57,57,57,57,57,57,57,57,56,56,50,54,50,101,
81 | 43,48,48,0,0,0,0,0
82 | ], 'failed 3.14158');
83 |
84 | expect(Bert.binary_to_list(Bert.encode(-3.14159))).to.deep.equal([
85 | 131,99,45,51,46,49,52,49,53,56,57,57,57,57,57,57,57,57,57,57,56,56,50,54,50,
86 | 101,43,48,48,0,0,0,0
87 | ], 'failed -3.14159');
88 |
89 | expect(Bert.binary_to_list(Bert.encode(0.0012))).to.deep.equal([
90 | 131,99,49,46,49,57,57,57,57,57,57,57,57,57,57,57,57,57,57,56,57,52,56,56,101,
91 | 45,48,51,0,0,0,0,0
92 | ]);
93 |
94 | expect(Bert.binary_to_list(Bert.encode(-0.0012))).to.deep.equal([
95 | 131,99,45,49,46,49,57,57,57,57,57,57,57,57,57,57,57,57,57,57,56,57,52,56,56,
96 | 101,45,48,51,0,0,0,0
97 | ]);
98 | });
99 |
100 | it ('should encode arrays', function() {
101 | expect(Bert.binary_to_list(Bert.encode(["1","2","3"]))).to.deep.equal([
102 | 131,108,0,0,0,3,107,0,1,49,107,0,1,50,107,0,1,51,106
103 | ]);
104 | });
105 |
106 | it ('should encode assoc arrays', function(){
107 | expect(Bert.binary_to_list(Bert.encode({a : 1, b : 2, c : 3}))).to.deep.equal([
108 | 131,108,0,0,0,3,104,2,100,0,1,97,97,1,104,2,100,0,1,98,97,2,104,2,100,0,1,99,97,3,106
109 | ])
110 | });
111 | it ('should encode tuple', function(){
112 | expect(Bert.binary_to_list(Bert.encode(Bert.tuple("Hello", 1)))).to.deep.equal([
113 | 131,104,2,107,0,5,72,101,108,108,111,97,1
114 | ])
115 | });
116 | it ('should encode empty list', function(){
117 | expect(Bert.binary_to_list(Bert.encode([]))).to.deep.equal([
118 | 131,104,2,100,0,4,98,101,114,116,100,0,3,110,105,108
119 | ])
120 | });
121 | it ('should encode complex', function(){
122 | expect(Bert.binary_to_list(Bert.encode({
123 | a : Bert.tuple(1, 2, 3),
124 | b : [400, 5, 6]
125 | }))).to.deep.equal([
126 | 131,108,0,0,0,2,104,2,100,0,1,97,104,3,97,1,97,2,97,3,104,2,100,0,1,98,108,0,0,0,3,98,0,0,1,144,97,5,97,6,106,106
127 | ])
128 | });
129 | it ('should decode complex', function(){
130 |
131 | var term = Bert.decode(Bert.bytes_to_string([131, 108, 0, 0, 0, 4, 104, 2, 100, 0, 4, 97, 116, 111, 109, 100, 0, 6, 109, 121, 65, 116, 111, 109, 104, 2, 100, 0, 6, 98, 105, 110, 97, 114, 121, 109, 0, 0, 0, 9, 77, 121, 32, 66, 105, 110, 97, 114, 121, 104, 2, 100, 0, 4, 98, 111, 111, 108, 100, 0, 4, 116, 114, 117, 101, 104, 2, 100, 0, 6, 115, 116, 114, 105, 110, 103, 107, 0, 11, 72, 101, 108, 108, 111, 32, 116, 104, 101, 114, 101, 106]))
132 | expect(Bert.pp_term(term)).to.equal('{atom, myAtom},{binary, <<"My Binary">>},{bool, true},{string, Hello there}')
133 |
134 | });
135 | it ('should decode small ints', function(){
136 | expect(Bert.decode(Bert.bytes_to_string([131,97,130]))).to.equal(130)
137 | })
138 | it ('should decode negative ints', function(){
139 | expect(Bert.decode(Bert.bytes_to_string([131,98,255,255,255,255]))).to.equal(-1)
140 | });
141 | it ('should decode ints', function(){
142 | var term = Bert.decode(Bert.bytes_to_string([131, 108, 0, 0, 0, 5, 104, 2, 100, 0, 13, 115, 109, 97, 108, 108, 95, 105, 110, 116, 101, 103, 101, 114, 97, 42, 104, 2, 100, 0, 8, 105, 110, 116, 101, 103, 101, 114, 49, 98, 0, 0, 19, 136, 104, 2, 100, 0, 8, 105, 110, 116, 101, 103, 101, 114, 50, 98, 255, 255, 236, 120, 104, 2, 100, 0, 8, 98, 105, 103, 95, 105, 110, 116, 49, 110, 4, 0, 177, 104, 222, 58, 104, 2, 100, 0, 8, 98, 105, 103, 95, 105, 110, 116, 50, 110, 4, 1, 177, 104, 222, 58, 106]));
143 | expect(Bert.pp_term(term)).to.equal('{small_integer, 42},{integer1, 5000},{integer2, -5000},{big_int1, 987654321},{big_int2, -987654321}')
144 | });
145 | it ('should decode floats', function(){
146 | // Try decoding this: -3.14159
147 | var term = Bert.decode(Bert.bytes_to_string([131,99,45,51,46,49,52,49,53,56,57,57,57,57,57,57,57,57,57,57,56,56,50,54,50,101,43,48,48,0,0,0,0]));
148 | expect(term).to.equal(-3.14159);
149 | });
150 | it ('should decode empty list', function(){
151 | var term = Bert.decode(Bert.bytes_to_string([131, 106]));
152 | expect(term).to.deep.equal([]);
153 | });
154 | it ('should decode true', function(){
155 | var term = Bert.decode(Bert.bytes_to_string([131,104,2,100,0,4,98,101,114,116,100,0,4,116,114,117,101]));
156 | expect(term).to.equal(true);
157 | })
158 | it ('should decode false', function(){
159 | var term = Bert.decode(Bert.bytes_to_string([131,104,2,100,0,4,98,101,114,116,100,0,5,102,97,108,115,101]));
160 | expect(term).to.equal(false);
161 | })
162 |
163 | });
164 |
--------------------------------------------------------------------------------