bert-1.1.6/0000755000175000017500000000000012440122105012312 5ustar rbalintrbalintbert-1.1.6/ext/0000755000175000017500000000000012440122105013112 5ustar rbalintrbalintbert-1.1.6/ext/bert/0000755000175000017500000000000012440122105014046 5ustar rbalintrbalintbert-1.1.6/ext/bert/c/0000755000175000017500000000000012440122105014270 5ustar rbalintrbalintbert-1.1.6/ext/bert/c/extconf.rb0000644000175000017500000000040412440122105016261 0ustar rbalintrbalint# Loads mkmf which is used to make makefiles for Ruby extensions require 'mkmf' # warnings save lives $CFLAGS << " -Wall " # Give it a name extension_name = 'decode' # The destination dir_config(extension_name) # Do the work create_makefile(extension_name)bert-1.1.6/ext/bert/c/decode.c0000644000175000017500000002700112440122105015657 0ustar rbalintrbalint#include "ruby.h" #include #include #define ERL_SMALL_INT 97 #define ERL_INT 98 #define ERL_FLOAT 99 #define ERL_ATOM 100 #define ERL_SMALL_TUPLE 104 #define ERL_LARGE_TUPLE 105 #define ERL_NIL 106 #define ERL_STRING 107 #define ERL_LIST 108 #define ERL_BIN 109 #define ERL_SMALL_BIGNUM 110 #define ERL_LARGE_BIGNUM 111 #define ERL_VERSION 131 #define BERT_VALID_TYPE(t) ((t) >= ERL_SMALL_INT && (t) <= ERL_LARGE_BIGNUM) #define BERT_TYPE_OFFSET (ERL_SMALL_INT) static VALUE rb_mBERT; static VALUE rb_cDecode; static VALUE rb_cTuple; struct bert_buf { const uint8_t *data; const uint8_t *end; }; static VALUE bert_read_invalid(struct bert_buf *buf); static VALUE bert_read_sint(struct bert_buf *buf); static VALUE bert_read_int(struct bert_buf *buf); static VALUE bert_read_float(struct bert_buf *buf); static VALUE bert_read_atom(struct bert_buf *buf); static VALUE bert_read_stuple(struct bert_buf *buf); static VALUE bert_read_ltuple(struct bert_buf *buf); static VALUE bert_read_nil(struct bert_buf *buf); static VALUE bert_read_string(struct bert_buf *buf); static VALUE bert_read_list(struct bert_buf *buf); static VALUE bert_read_bin(struct bert_buf *buf); static VALUE bert_read_sbignum(struct bert_buf *buf); static VALUE bert_read_lbignum(struct bert_buf *buf); typedef VALUE (*bert_ptr)(struct bert_buf *buf); static bert_ptr bert_callbacks[] = { &bert_read_sint, &bert_read_int, &bert_read_float, &bert_read_atom, &bert_read_invalid, &bert_read_invalid, &bert_read_invalid, &bert_read_stuple, &bert_read_ltuple, &bert_read_nil, &bert_read_string, &bert_read_list, &bert_read_bin, &bert_read_sbignum, &bert_read_lbignum }; static inline uint8_t bert_buf_read8(struct bert_buf *buf) { return *buf->data++; } static inline uint16_t bert_buf_read16(struct bert_buf *buf) { /* Note that this will trigger -Wcast-align and throw a * bus error on platforms where unaligned reads are not * allowed. Also note that this is not breaking any * strict aliasing rules. */ uint16_t short_val = *(uint16_t *)buf->data; buf->data += sizeof(uint16_t); return ntohs(short_val); } static inline uint32_t bert_buf_read32(struct bert_buf *buf) { /* Note that this will trigger -Wcast-align and throw a * bus error on platforms where unaligned reads are not * allowed. Also note that this is not breaking any * strict aliasing rules. */ uint32_t long_val = *(uint32_t *)buf->data; buf->data += sizeof(uint32_t); return ntohl(long_val); } static inline void bert_buf_ensure(struct bert_buf *buf, size_t size) { if (buf->data + size > buf->end) rb_raise(rb_eEOFError, "Unexpected end of BERT stream"); } static VALUE bert_read(struct bert_buf *buf) { uint8_t type; bert_buf_ensure(buf, 1); type = bert_buf_read8(buf); if (!BERT_VALID_TYPE(type)) rb_raise(rb_eRuntimeError, "Invalid tag '%d' for term", type); return bert_callbacks[type - BERT_TYPE_OFFSET](buf); } static VALUE bert_read_dict(struct bert_buf *buf) { uint8_t type; uint32_t length = 0, i; VALUE rb_dict; bert_buf_ensure(buf, 1); type = bert_buf_read8(buf); if (type != ERL_LIST && type != ERL_NIL) rb_raise(rb_eTypeError, "Invalid dict spec, not an erlang list"); if (type == ERL_LIST) { bert_buf_ensure(buf, 4); length = bert_buf_read32(buf); } rb_dict = rb_hash_new(); for (i = 0; i < length; ++i) { VALUE key, val; bert_buf_ensure(buf, 2); if (bert_buf_read8(buf) != ERL_SMALL_TUPLE || bert_buf_read8(buf) != 2) rb_raise(rb_eTypeError, "Invalid dict tuple"); key = bert_read(buf); val = bert_read(buf); rb_hash_aset(rb_dict, key, val); } if (type == ERL_LIST) { /* disregard tail; adquire women */ bert_buf_ensure(buf, 1); (void)bert_buf_read8(buf); } return rb_dict; } static inline void bert_ensure_arity(uint32_t arity, uint32_t expected) { if (arity != expected) rb_raise(rb_eTypeError, "Invalid tuple arity for complex type"); } static VALUE bert_read_complex(struct bert_buf *buf, uint32_t arity) { VALUE rb_type; ID id_type; rb_type = bert_read(buf); Check_Type(rb_type, T_SYMBOL); id_type = SYM2ID(rb_type); if (id_type == rb_intern("nil")) { bert_ensure_arity(arity, 2); return Qnil; } else if (id_type == rb_intern("true")) { bert_ensure_arity(arity, 2); return Qtrue; } else if (id_type == rb_intern("false")) { bert_ensure_arity(arity, 2); return Qfalse; } else if (id_type == rb_intern("time")) { VALUE rb_megasecs, rb_secs, rb_microsecs, rb_stamp, rb_msecs; bert_ensure_arity(arity, 5); rb_megasecs = bert_read(buf); rb_secs = bert_read(buf); rb_microsecs = bert_read(buf); rb_msecs = rb_funcall(rb_megasecs, rb_intern("*"), 1, INT2NUM(1000000)); rb_stamp = rb_funcall(rb_msecs, rb_intern("+"), 1, rb_secs); return rb_funcall(rb_cTime, rb_intern("at"), 2, rb_stamp, rb_microsecs); } else if (id_type == rb_intern("regex")) { VALUE rb_source, rb_opts; int flags = 0; bert_ensure_arity(arity, 4); rb_source = bert_read(buf); rb_opts = bert_read(buf); Check_Type(rb_source, T_STRING); Check_Type(rb_opts, T_ARRAY); if (rb_ary_includes(rb_opts, ID2SYM(rb_intern("caseless")))) flags = flags | 1; if (rb_ary_includes(rb_opts, ID2SYM(rb_intern("extended")))) flags = flags | 2; if (rb_ary_includes(rb_opts, ID2SYM(rb_intern("multiline")))) flags = flags | 4; return rb_funcall(rb_cRegexp, rb_intern("new"), 2, rb_source, INT2NUM(flags)); } else if (id_type == rb_intern("dict")) { bert_ensure_arity(arity, 3); return bert_read_dict(buf); } rb_raise(rb_eTypeError, "Invalid tag for complex value"); return Qnil; } static VALUE bert_read_tuple(struct bert_buf *buf, uint32_t arity) { if (arity > 0) { VALUE rb_tag = bert_read(buf); if (TYPE(rb_tag) == T_SYMBOL && SYM2ID(rb_tag) == rb_intern("bert")) { return bert_read_complex(buf, arity); } else { uint32_t i; VALUE rb_tuple; rb_tuple = rb_funcall(rb_cTuple, rb_intern("new"), 1, INT2NUM(arity)); rb_ary_store(rb_tuple, 0, rb_tag); for(i = 1; i < arity; ++i) rb_ary_store(rb_tuple, i, bert_read(buf)); return rb_tuple; } } return rb_funcall(rb_cTuple, rb_intern("new"), 0); } static VALUE bert_read_stuple(struct bert_buf *buf) { bert_buf_ensure(buf, 1); return bert_read_tuple(buf, bert_buf_read8(buf)); } static VALUE bert_read_ltuple(struct bert_buf *buf) { bert_buf_ensure(buf, 4); return bert_read_tuple(buf, bert_buf_read32(buf)); } static VALUE bert_read_list(struct bert_buf *buf) { uint32_t i, length; VALUE rb_list; bert_buf_ensure(buf, 4); length = bert_buf_read32(buf); rb_list = rb_ary_new2(length); for(i = 0; i < length; ++i) rb_ary_store(rb_list, i, bert_read(buf)); /* disregard tail; adquire currency */ bert_buf_ensure(buf, 1); (void)bert_buf_read8(buf); return rb_list; } static VALUE bert_read_bin(struct bert_buf *buf) { uint32_t length; VALUE rb_bin; bert_buf_ensure(buf, 4); length = bert_buf_read32(buf); bert_buf_ensure(buf, length); rb_bin = rb_str_new((char *)buf->data, length); buf->data += length; return rb_bin; } static VALUE bert_read_string(struct bert_buf *buf) { uint16_t i, length; VALUE rb_string; bert_buf_ensure(buf, 2); length = bert_buf_read16(buf); bert_buf_ensure(buf, length); rb_string = rb_ary_new2(length); for (i = 0; i < length; ++i) rb_ary_store(rb_string, i, INT2FIX(buf->data[i])); buf->data += length; return rb_string; } static VALUE bert_read_atom(struct bert_buf *buf) { VALUE rb_atom; uint32_t atom_len; bert_buf_ensure(buf, 2); atom_len = bert_buf_read16(buf); /* Instead of trying to build the symbol * from here, just create a Ruby string * and internalize it. this will be faster for * unique symbols */ bert_buf_ensure(buf, atom_len); rb_atom = rb_str_new((char *)buf->data, atom_len); buf->data += atom_len; return rb_str_intern(rb_atom); } static VALUE bert_read_sint(struct bert_buf *buf) { bert_buf_ensure(buf, 1); return INT2FIX((uint8_t)bert_buf_read8(buf)); } static VALUE bert_read_int(struct bert_buf *buf) { bert_buf_ensure(buf, 4); return LONG2NUM((int32_t)bert_buf_read32(buf)); } static VALUE bert_buf_tobignum(struct bert_buf *buf, uint8_t sign, uint32_t bin_digits) { #ifdef BERT_FAST_BIGNUM uint32_t *bin_buf = NULL; VALUE rb_num; uint32_t round_size; bert_buf_ensure(buf, bin_digits); /* Hack: ensure that we have at least a full word * of extra padding for the actual string, so Ruby * cannot guess the sign of the bigint from the MSB */ round_size = 4 + ((bin_digits + 3) & ~3); bin_buf = xmalloc(round_size); memcpy(bin_buf, buf->data, bin_digits); memset((char *)bin_buf + bin_digits, 0x0, round_size - bin_digits); /* Make Ruby unpack the string internally. * this is significantly faster than adding * the bytes one by one */ rb_num = rb_big_unpack(bin_buf, round_size / 4); /* Enfore sign. So fast! */ RBIGNUM_SET_SIGN(rb_num, !sign); free(bin_buf); return rb_num; #else /** * Slower bignum serialization; convert to a base16 * string and then let ruby parse it internally. * * We're shipping with this by default because * `rb_big_unpack` is not trustworthy */ static const char to_hex[] = "0123456789abcdef"; char *num_str = NULL, *ptr; VALUE rb_num; int32_t i; bert_buf_ensure(buf, bin_digits); /* 2 digits per byte + sign + trailing null */ num_str = ptr = xmalloc((bin_digits * 2) + 2); *ptr++ = sign ? '-' : '+'; for (i = (int32_t)bin_digits - 1; i >= 0; --i) { uint8_t val = buf->data[i]; *ptr++ = to_hex[val >> 4]; *ptr++ = to_hex[val & 0xf]; } *ptr = 0; buf->data += bin_digits; rb_num = rb_cstr_to_inum(num_str, 16, 1); free(num_str); return rb_num; #endif } VALUE bert_read_sbignum(struct bert_buf *buf) { uint8_t sign, bin_digits; bert_buf_ensure(buf, 2); bin_digits = bert_buf_read8(buf); sign = bert_buf_read8(buf); return bert_buf_tobignum(buf, sign, (uint32_t)bin_digits); } VALUE bert_read_lbignum(struct bert_buf *buf) { uint32_t bin_digits; uint8_t sign; bert_buf_ensure(buf, 5); bin_digits = bert_buf_read32(buf); sign = bert_buf_read8(buf); return bert_buf_tobignum(buf, sign, bin_digits); } /* * ------------------- * |1 | 31 | * |99 | Float String| * ------------------- * * A float is stored in string format. the format used in sprintf * to format the float is "%.20e" (there are more bytes allocated * than necessary). To unpack the float use sscanf with format "%lf". */ static VALUE bert_read_float(struct bert_buf *buf) { VALUE rb_float; bert_buf_ensure(buf, 31); rb_float = rb_str_new((char *)buf->data, 31); buf->data += 31; return rb_funcall(rb_float, rb_intern("to_f"), 0); } static VALUE bert_read_nil(struct bert_buf *buf) { return rb_ary_new2(0); } static VALUE bert_read_invalid(struct bert_buf *buf) { rb_raise(rb_eTypeError, "Invalid object tag in BERT stream"); return Qnil; } static VALUE rb_bert_decode(VALUE klass, VALUE rb_string) { struct bert_buf buf; Check_Type(rb_string, T_STRING); buf.data = (uint8_t *)RSTRING_PTR(rb_string); buf.end = buf.data + RSTRING_LEN(rb_string); bert_buf_ensure(&buf, 1); if (bert_buf_read8(&buf) != ERL_VERSION) rb_raise(rb_eTypeError, "Invalid magic value for BERT string"); return bert_read(&buf); } static VALUE rb_bert_impl(VALUE klass) { return rb_str_new("C", 1); } void Init_decode() { rb_mBERT = rb_const_get(rb_cObject, rb_intern("BERT")); rb_cTuple = rb_const_get(rb_mBERT, rb_intern("Tuple")); rb_cDecode = rb_define_class_under(rb_mBERT, "Decode", rb_cObject); rb_define_singleton_method(rb_cDecode, "decode", rb_bert_decode, 1); rb_define_singleton_method(rb_cDecode, "impl", rb_bert_impl, 0); } bert-1.1.6/metadata.yml0000644000175000017500000000403112440122105014613 0ustar rbalintrbalint--- !ruby/object:Gem::Specification name: bert version: !ruby/object:Gem::Version hash: 31 prerelease: segments: - 1 - 1 - 6 version: 1.1.6 platform: ruby authors: - Tom Preston-Werner autorequire: bindir: bin cert_chain: [] date: 2010-02-08 00:00:00 -08:00 default_executable: dependencies: - !ruby/object:Gem::Dependency name: thoughtbot-shoulda prerelease: false requirement: &id001 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" type: :development version_requirements: *id001 description: BERT Serializiation for Ruby email: tom@mojombo.com executables: [] extensions: - ext/bert/c/extconf.rb - ext/bert/c/extconf.rb extra_rdoc_files: - LICENSE - README.md files: - .document - .gitignore - History.txt - LICENSE - README.md - Rakefile - VERSION - bench/bench.rb - bench/decode_bench.rb - bench/encode_bench.rb - bench/results.txt - bert.gemspec - ext/bert/c/decode.c - ext/bert/c/extconf.rb - lib/bert.rb - lib/bert/bert.rb - lib/bert/decode.rb - lib/bert/decoder.rb - lib/bert/encode.rb - lib/bert/encoder.rb - lib/bert/types.rb - test/bert_test.rb - test/decoder_test.rb - test/encoder_test.rb - test/test_helper.rb has_rdoc: true homepage: http://github.com/mojombo/bert licenses: [] post_install_message: rdoc_options: - --charset=UTF-8 require_paths: - lib - ext required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: rubygems_version: 1.6.2 signing_key: specification_version: 3 summary: BERT Serializiation for Ruby test_files: - test/bert_test.rb - test/decoder_test.rb - test/encoder_test.rb - test/test_helper.rb bert-1.1.6/README.md0000644000175000017500000000334512440122105013576 0ustar rbalintrbalintBERT ==== A BERT (Binary ERlang Term) serialization library for Ruby. It can encode Ruby objects into BERT format and decode BERT binaries into Ruby objects. See the BERT specification at [bert-rpc.org](http://bert-rpc.org). Instances of the following Ruby classes will be automatically converted to the proper simple BERT type: * Fixnum * Float * Symbol * Array * String Instances of the following Ruby classes will be automatically converted to the proper complex BERT type: * NilClass * TrueClass * FalseClass * Hash * Time * Regexp To designate tuples, simply prefix an Array literal with a `t` or use the BERT::Tuple class: t[:foo, [1, 2, 3]] BERT::Tuple[:foo, [1, 2, 3]] Both of these will be converted to (in Erlang syntax): {foo, [1, 2, 3]} Installation ------------ gem install bert -s http://gemcutter.org Usage ----- require 'bert' bert = BERT.encode(t[:user, {:name => 'TPW', :nick => 'mojombo'}]) # => "\203h\002d\000\004userh\003d\000\004bertd\000\004dictl\000\000\ 000\002h\002d\000\004namem\000\000\000\003TPWh\002d\000\004nickm\ 000\000\000\amojomboj" BERT.decode(bert) # => t[:user, {:name=>"TPW", :nick=>"mojombo"}] Note on Patches/Pull Requests ----------------------------- * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. Copyright --------- Copyright (c) 2009 Tom Preston-Werner. See LICENSE for details. bert-1.1.6/LICENSE0000644000175000017500000000204612440122105013321 0ustar rbalintrbalintCopyright (c) 2009 Tom Preston-Werner 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. bert-1.1.6/bench/0000755000175000017500000000000012440122105013371 5ustar rbalintrbalintbert-1.1.6/bench/results.txt0000644000175000017500000000413312440122105015634 0ustar rbalintrbalint user system total real Comparisons JSON tiny 0.020000 0.000000 0.020000 ( 0.017486) JSON small 0.070000 0.000000 0.070000 ( 0.080681) JSON large 15.260000 0.600000 15.860000 ( 16.427857) JSON complex 1.470000 0.010000 1.480000 ( 1.558230) YAJL tiny 0.010000 0.000000 0.010000 ( 0.015537) YAJL small 0.050000 0.000000 0.050000 ( 0.061879) YAJL large 3.610000 0.800000 4.410000 ( 4.675255) YAJL complex 1.030000 0.000000 1.030000 ( 1.066871) Ruby tiny 0.010000 0.000000 0.010000 ( 0.007117) Ruby small 0.020000 0.000000 0.020000 ( 0.015964) Ruby large 0.040000 0.000000 0.040000 ( 0.042695) Ruby complex 0.040000 0.000000 0.040000 ( 0.048395) Ruby encoder / Ruby decoder 41503465479e8762916d6997d91639f0d7308a13 BERT tiny 0.090000 0.000000 0.090000 ( 0.092357) BERT small 0.830000 0.000000 0.830000 ( 0.853270) BERT large 4.190000 0.620000 4.810000 ( 4.959149) BERT complex 19.380000 0.080000 19.460000 ( 20.402862) Simple C decoder / Ruby encoder 41503465479e8762916d6997d91639f0d7308a13 BERT tiny 0.030000 0.000000 0.030000 ( 0.033826) BERT small 0.390000 0.010000 0.400000 ( 0.413229) BERT large 2.270000 0.550000 2.820000 ( 3.029141) BERT complex 8.680000 0.040000 8.720000 ( 9.097990) Smarter Ruby decoder BERT tiny 0.070000 0.000000 0.070000 ( 0.075155) BERT small 0.810000 0.010000 0.820000 ( 0.831905) BERT large 4.340000 0.600000 4.940000 ( 5.064875) BERT complex 18.460000 0.070000 18.530000 ( 19.096184) Smarter C decoder BERT tiny 0.030000 0.000000 0.030000 ( 0.035685) BERT small 0.350000 0.010000 0.360000 ( 0.358929) BERT large 2.410000 0.560000 2.970000 ( 3.056593) BERT complex 7.910000 0.040000 7.950000 ( 8.236641) Smart C Decoder only BERT tiny 0.000000 0.000000 0.000000 ( 0.001820) BERT small 0.000000 0.000000 0.000000 ( 0.003859) BERT large 0.430000 0.010000 0.440000 ( 0.499631) BERT complex 0.080000 0.010000 0.090000 ( 0.086992) bert-1.1.6/bench/encode_bench.rb0000644000175000017500000000264612440122105016322 0ustar rbalintrbalint$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'rubygems' require 'bert' require 'json' require 'yajl' require 'benchmark' ITER = 1_000 tiny = t[:ok, :awesome] small = t[:ok, :answers, [42] * 42] large = ["abc" * 1000] * 100 complex = [42, {:foo => 'bac' * 100}, t[(1..100).to_a]] * 10 Benchmark.bm(13) do |bench| bench.report("BERT tiny") {ITER.times {BERT.encode(tiny)}} bench.report("BERT small") {ITER.times {BERT.encode(small)}} bench.report("BERT large") {ITER.times {BERT.encode(large)}} bench.report("BERT complex") {ITER.times {BERT.encode(complex)}} puts bench.report("JSON tiny") {ITER.times {JSON.dump(tiny)}} bench.report("JSON small") {ITER.times {JSON.dump(small)}} bench.report("JSON large") {ITER.times {JSON.dump(large)}} bench.report("JSON complex") {ITER.times {JSON.dump(complex)}} puts bench.report("JSON tiny") {ITER.times {Yajl::Encoder.encode(tiny)}} bench.report("JSON small") {ITER.times {Yajl::Encoder.encode(small)}} bench.report("JSON large") {ITER.times {Yajl::Encoder.encode(large)}} bench.report("JSON complex") {ITER.times {Yajl::Encoder.encode(complex)}} puts bench.report("Ruby tiny") {ITER.times {Marshal.dump(tiny)}} bench.report("Ruby small") {ITER.times {Marshal.dump(small)}} bench.report("Ruby large") {ITER.times {Marshal.dump(large)}} bench.report("Ruby complex") {ITER.times {Marshal.dump(complex)}} endbert-1.1.6/bench/bench.rb0000644000175000017500000000313412440122105014776 0ustar rbalintrbalint$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'rubygems' require 'bert' require 'json' require 'yajl' require 'benchmark' ITER = 1_000 tiny = t[:ok, :awesome] small = t[:ok, :answers, [42] * 42] large = ["abc" * 1000] * 100 complex = [42, {:foo => 'bac' * 100}, t[(1..100).to_a]] * 10 Benchmark.bm do |bench| bench.report("BERT tiny") {ITER.times {BERT.decode(BERT.encode(tiny))}} bench.report("BERT small") {ITER.times {BERT.decode(BERT.encode(small))}} bench.report("BERT large") {ITER.times {BERT.decode(BERT.encode(large))}} bench.report("BERT complex") {ITER.times {BERT.decode(BERT.encode(complex))}} bench.report("JSON tiny") {ITER.times {JSON.load(JSON.dump(tiny))}} bench.report("JSON small") {ITER.times {JSON.load(JSON.dump(small))}} bench.report("JSON large") {ITER.times {JSON.load(JSON.dump(large))}} bench.report("JSON complex") {ITER.times {JSON.load(JSON.dump(complex))}} bench.report("YAJL tiny") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(tiny))}} bench.report("YAJL small") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(small))}} bench.report("YAJL large") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(large))}} bench.report("YAJL complex") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(complex))}} bench.report("Ruby tiny") {ITER.times {Marshal.load(Marshal.dump(tiny))}} bench.report("Ruby small") {ITER.times {Marshal.load(Marshal.dump(small))}} bench.report("Ruby large") {ITER.times {Marshal.load(Marshal.dump(large))}} bench.report("Ruby complex") {ITER.times {Marshal.load(Marshal.dump(complex))}} endbert-1.1.6/bench/decode_bench.rb0000644000175000017500000000645612440122105016313 0ustar rbalintrbalint$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'rubygems' require 'json' require 'yajl' require 'benchmark' ITER = 1_000 def setup tiny = t[:ok, :awesome] small = t[:ok, :answers, [42] * 42] large = ["abc" * 1000] * 100 complex = [42, {:foo => 'bac' * 100}, t[(1..100).to_a]] * 10 $tiny_encoded_bert = BERT.encode(tiny) $small_encoded_bert = BERT.encode(small) $large_encoded_bert = BERT.encode(large) $complex_encoded_bert = BERT.encode(complex) $tiny_encoded_json = JSON.dump(tiny) $small_encoded_json = JSON.dump(small) $large_encoded_json = JSON.dump(large) $complex_encoded_json = JSON.dump(complex) $tiny_encoded_yajl = Yajl::Encoder.encode(tiny) $small_encoded_yajl = Yajl::Encoder.encode(small) $large_encoded_yajl = Yajl::Encoder.encode(large) $complex_encoded_yajl = Yajl::Encoder.encode(complex) $tiny_encoded_ruby = Marshal.dump(tiny) $small_encoded_ruby = Marshal.dump(small) $large_encoded_ruby = Marshal.dump(large) $complex_encoded_ruby = Marshal.dump(complex) end Benchmark.bm(13) do |bench| pid = fork do Dir.chdir(File.join(File.dirname(__FILE__), *%w[.. ext bert c])) { `make` } require 'bert' raise "Could not load C extension" unless BERT::Decode.impl == 'C' setup puts "BERT C Extension Decoder" bench.report("BERT tiny") {ITER.times {BERT.decode($tiny_encoded_bert)}} bench.report("BERT small") {ITER.times {BERT.decode($small_encoded_bert)}} bench.report("BERT large") {ITER.times {BERT.decode($large_encoded_bert)}} bench.report("BERT complex") {ITER.times {BERT.decode($complex_encoded_bert)}} puts end Process.waitpid(pid) pid = fork do Dir.chdir(File.join(File.dirname(__FILE__), *%w[.. ext bert c])) do ['*.bundle', '*.o'].each { |pat| `rm -f #{pat}` } end require 'bert' raise "Not using Ruby decoder" unless BERT::Decode.impl == 'Ruby' setup puts "BERT Pure Ruby Decoder" bench.report("BERT tiny") {ITER.times {BERT.decode($tiny_encoded_bert)}} bench.report("BERT small") {ITER.times {BERT.decode($small_encoded_bert)}} bench.report("BERT large") {ITER.times {BERT.decode($large_encoded_bert)}} bench.report("BERT complex") {ITER.times {BERT.decode($complex_encoded_bert)}} puts end Process.waitpid(pid) require 'bert' setup bench.report("JSON tiny") {ITER.times {JSON.load($tiny_encoded_json)}} bench.report("JSON small") {ITER.times {JSON.load($small_encoded_json)}} bench.report("JSON large") {ITER.times {JSON.load($large_encoded_json)}} bench.report("JSON complex") {ITER.times {JSON.load($complex_encoded_json)}} puts bench.report("YAJL tiny") {ITER.times {Yajl::Parser.parse($tiny_encoded_yajl)}} bench.report("YAJL small") {ITER.times {Yajl::Parser.parse($small_encoded_yajl)}} bench.report("YAJL large") {ITER.times {Yajl::Parser.parse($large_encoded_yajl)}} bench.report("YAJL complex") {ITER.times {Yajl::Parser.parse($complex_encoded_yajl)}} puts bench.report("Ruby tiny") {ITER.times {Marshal.load($tiny_encoded_ruby)}} bench.report("Ruby small") {ITER.times {Marshal.load($small_encoded_ruby)}} bench.report("Ruby large") {ITER.times {Marshal.load($large_encoded_ruby)}} bench.report("Ruby complex") {ITER.times {Marshal.load($complex_encoded_ruby)}} endbert-1.1.6/History.txt0000644000175000017500000000177412440122105014525 0ustar rbalintrbalint= 1.1.6 / 2012-05-25 * Bug fixes * Better handling of utf-8 characters = 1.1.5 / 2011-12-09 * Bug fixes * Faster and more secure C BERT decoder * Fix for encoding of negative bignums * Ruby 1.9 compatibility = 1.1.2 / 2010-02-08 * Bug fixes * Fix bignum handling on 256 byte boundary * Remove unnecessary rubygems require = 1.1.1 / 2010-01-12 * Bug fixes * require 'stringio' * Fix number encoding problem on 32 bit arch = 1.1.0 / 2009-10-27 * Major Changes * Remove reliance on Erlectricity. * Bug fixes * Fix unsigned int problem in C decoder * Fix stack overflow segfault in C binary decoder for > 8MB binaries * Optimize C bytelist decoder * Fix bignum encoding = 1.0.0 / 2009-10-19 * No changes. Production ready! = 0.2.0 / 2009-10-15 * Major changes * Use {time, MegaSecs, Secs, Microsecs} for time serialization * Use array of options for regex serialization * Tests * Add roundtrip tests = 0.1.0 / 2009-10-08 * Birthday! bert-1.1.6/lib/0000755000175000017500000000000012440122105013060 5ustar rbalintrbalintbert-1.1.6/lib/bert.rb0000644000175000017500000000066712440122105014352 0ustar rbalintrbalint require 'stringio' $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext]) require 'bert/bert' require 'bert/types' begin # try to load the C extension require 'bert/c/decode' rescue LoadError # fall back on the pure ruby version require 'bert/decode' end require 'bert/encode' require 'bert/encoder' require 'bert/decoder' # Global method for specifying that an array should be encoded as a tuple. def t BERT::Tuple endbert-1.1.6/lib/bert/0000755000175000017500000000000012440122105014014 5ustar rbalintrbalintbert-1.1.6/lib/bert/decoder.rb0000644000175000017500000000031512440122105015745 0ustar rbalintrbalintmodule BERT class Decoder # Decode a BERT into a Ruby object. # +bert+ is the BERT String # # Returns a Ruby object def self.decode(bert) Decode.decode(bert) end end endbert-1.1.6/lib/bert/bert.rb0000644000175000017500000000047412440122105015302 0ustar rbalintrbalintmodule BERT def self.encode(ruby) Encoder.encode(ruby) end def self.decode(bert) Decoder.decode(bert) end def self.ebin(str) bytes = [] str.each_byte { |b| bytes << b.to_s } "<<" + bytes.join(',') + ">>" end class Tuple < Array def inspect "t#{super}" end end endbert-1.1.6/lib/bert/encoder.rb0000644000175000017500000000241412440122105015761 0ustar rbalintrbalintmodule BERT class Encoder # Encode a Ruby object into a BERT. # +ruby+ is the Ruby object # # Returns a BERT def self.encode(ruby) complex_ruby = convert(ruby) Encode.encode(complex_ruby) end # Convert complex Ruby form in simple Ruby form. # +item+ is the Ruby object to convert # # Returns the converted Ruby object def self.convert(item) case item when Hash pairs = [] item.each_pair { |k, v| pairs << t[convert(k), convert(v)] } t[:bert, :dict, pairs] when Tuple Tuple.new(item.map { |x| convert(x) }) when Array item.map { |x| convert(x) } when nil t[:bert, :nil] when TrueClass t[:bert, :true] when FalseClass t[:bert, :false] when Time t[:bert, :time, item.to_i / 1_000_000, item.to_i % 1_000_000, item.usec] when Regexp options = [] options << :caseless if item.options & Regexp::IGNORECASE > 0 options << :extended if item.options & Regexp::EXTENDED > 0 options << :multiline if item.options & Regexp::MULTILINE > 0 t[:bert, :regex, item.source, options] else item end end end endbert-1.1.6/lib/bert/types.rb0000644000175000017500000000053412440122105015507 0ustar rbalintrbalintmodule BERT module Types SMALL_INT = 97 INT = 98 SMALL_BIGNUM = 110 LARGE_BIGNUM = 111 FLOAT = 99 ATOM = 100 SMALL_TUPLE = 104 LARGE_TUPLE = 105 NIL = 106 STRING = 107 LIST = 108 BIN = 109 FUN = 117 NEW_FUN = 112 MAGIC = 131 MAX_INT = (1 << 27) -1 MIN_INT = -(1 << 27) end endbert-1.1.6/lib/bert/encode.rb0000644000175000017500000000533012440122105015577 0ustar rbalintrbalintmodule BERT class Encode include Types attr_accessor :out def initialize(out) self.out = out end def self.encode(data) io = StringIO.new io.set_encoding('binary') if io.respond_to?(:set_encoding) self.new(io).write_any(data) io.string end def write_any obj write_1 MAGIC write_any_raw obj end def write_any_raw obj case obj when Symbol then write_symbol(obj) when Fixnum, Bignum then write_fixnum(obj) when Float then write_float(obj) when Tuple then write_tuple(obj) when Array then write_list(obj) when String then write_binary(obj) else fail(obj) end end def write_1(byte) out.write([byte].pack("C")) end def write_2(short) out.write([short].pack("n")) end def write_4(long) out.write([long].pack("N")) end def write_string(string) out.write(string) end def write_boolean(bool) write_symbol(bool.to_s.to_sym) end def write_symbol(sym) fail(sym) unless sym.is_a?(Symbol) data = sym.to_s write_1 ATOM write_2 data.bytesize write_string data end def write_fixnum(num) if num >= 0 && num < 256 write_1 SMALL_INT write_1 num elsif num <= MAX_INT && num >= MIN_INT write_1 INT write_4 num else write_bignum num end end def write_float(float) write_1 FLOAT write_string format("%15.15e", float).ljust(31, "\000") end def write_bignum(num) n = (num.abs.to_s(2).size / 8.0).ceil if n < 256 write_1 SMALL_BIGNUM write_1 n write_bignum_guts(num) else write_1 LARGE_BIGNUM write_4 n write_bignum_guts(num) end end def write_bignum_guts(num) write_1 (num >= 0 ? 0 : 1) num = num.abs while num != 0 rem = num % 256 write_1 rem num = num >> 8 end end def write_tuple(data) fail(data) unless data.is_a? Array if data.length < 256 write_1 SMALL_TUPLE write_1 data.length else write_1 LARGE_TUPLE write_4 data.length end data.each { |e| write_any_raw e } end def write_list(data) fail(data) unless data.is_a? Array write_1 NIL and return if data.empty? write_1 LIST write_4 data.length data.each{|e| write_any_raw e } write_1 NIL end def write_binary(data) write_1 BIN write_4 data.bytesize write_string data end private def fail(obj) raise "Cannot encode to erlang external format: #{obj.inspect}" end end end bert-1.1.6/lib/bert/decode.rb0000644000175000017500000001337512440122105015575 0ustar rbalintrbalintmodule BERT class Decode attr_accessor :in include Types def self.impl 'Ruby' end def self.decode(string) io = StringIO.new(string) io.set_encoding('binary') if io.respond_to?(:set_encoding) new(io).read_any end def initialize(ins) @in = ins @peeked = "" end def read_any fail("Bad Magic") unless read_1 == MAGIC read_any_raw end def read_any_raw case peek_1 when ATOM then read_atom when SMALL_INT then read_small_int when INT then read_int when SMALL_BIGNUM then read_small_bignum when LARGE_BIGNUM then read_large_bignum when FLOAT then read_float when SMALL_TUPLE then read_small_tuple when LARGE_TUPLE then read_large_tuple when NIL then read_nil when STRING then read_erl_string when LIST then read_list when BIN then read_bin else fail("Unknown term tag: #{peek_1}") end end def read(length) if length < @peeked.length result = @peeked[0...length] @peeked = @peeked[length..-1] length = 0 else result = @peeked @peeked = '' length -= result.length end if length > 0 result << @in.read(length) end result end def peek(length) if length <= @peeked.length @peeked[0...length] else read_bytes = @in.read(length - @peeked.length) @peeked << read_bytes if read_bytes @peeked end end def peek_1 peek(1).unpack("C").first end def peek_2 peek(2).unpack("n").first end def read_1 read(1).unpack("C").first end def read_2 read(2).unpack("n").first end def read_4 read(4).unpack("N").first end def read_string(length) read(length) end def read_atom fail("Invalid Type, not an atom") unless read_1 == ATOM length = read_2 a = read_string(length) case a when "" Marshal.load("\004\b:\005") # Workaround for inability to do ''.to_sym else a.to_sym end end def read_small_int fail("Invalid Type, not a small int") unless read_1 == SMALL_INT read_1 end def read_int fail("Invalid Type, not an int") unless read_1 == INT value = read_4 negative = (value >> 31)[0] == 1 value = (value - (1 << 32)) if negative value end def read_small_bignum fail("Invalid Type, not a small bignum") unless read_1 == SMALL_BIGNUM size = read_1 sign = read_1 bytes = read_string(size).unpack("C" * size) added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index| byte, index = *byte_index value = (byte * (256 ** index)) sign != 0 ? (result - value) : (result + value) end added end def read_large_bignum fail("Invalid Type, not a large bignum") unless read_1 == LARGE_BIGNUM size = read_4 sign = read_1 bytes = read_string(size).unpack("C" * size) added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index| byte, index = *byte_index value = (byte * (256 ** index)) sign != 0 ? (result - value) : (result + value) end added end def read_float fail("Invalid Type, not a float") unless read_1 == FLOAT string_value = read_string(31) result = string_value.to_f end def read_small_tuple fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE read_tuple(read_1) end def read_large_tuple fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE read_tuple(read_4) end def read_tuple(arity) if arity > 0 tag = read_any_raw if tag == :bert read_complex_type(arity) else tuple = Tuple.new(arity) tuple[0] = tag (arity - 1).times { |i| tuple[i + 1] = read_any_raw } tuple end else Tuple.new end end def read_complex_type(arity) case read_any_raw when :nil nil when :true true when :false false when :time Time.at(read_any_raw * 1_000_000 + read_any_raw, read_any_raw) when :regex source = read_any_raw opts = read_any_raw options = 0 options |= Regexp::EXTENDED if opts.include?(:extended) options |= Regexp::IGNORECASE if opts.include?(:caseless) options |= Regexp::MULTILINE if opts.include?(:multiline) Regexp.new(source, options) when :dict read_dict else nil end end def read_dict type = read_1 fail("Invalid dict spec, not an erlang list") unless [LIST, NIL].include?(type) if type == LIST length = read_4 else length = 0 end hash = {} length.times do |i| pair = read_any_raw hash[pair[0]] = pair[1] end read_1 if type == LIST hash end def read_nil fail("Invalid Type, not a nil list") unless read_1 == NIL [] end def read_erl_string fail("Invalid Type, not an erlang string") unless read_1 == STRING length = read_2 read_string(length).unpack('C' * length) end def read_list fail("Invalid Type, not an erlang list") unless read_1 == LIST length = read_4 list = (0...length).map { |i| read_any_raw } read_1 list end def read_bin fail("Invalid Type, not an erlang binary") unless read_1 == BIN length = read_4 read_string(length) end def fail(str) raise str end end end bert-1.1.6/VERSION0000644000175000017500000000000612440122105013356 0ustar rbalintrbalint1.1.6 bert-1.1.6/bert.gemspec0000644000175000017500000000374012440122105014617 0ustar rbalintrbalint# Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = %q{bert} s.version = "1.1.6" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Tom Preston-Werner"] s.date = %q{2010-02-08} s.description = %q{BERT Serializiation for Ruby} s.email = %q{tom@mojombo.com} s.extensions = ["ext/bert/c/extconf.rb", "ext/bert/c/extconf.rb"] s.extra_rdoc_files = [ "LICENSE", "README.md" ] s.files = [ ".document", ".gitignore", "History.txt", "LICENSE", "README.md", "Rakefile", "VERSION", "bench/bench.rb", "bench/decode_bench.rb", "bench/encode_bench.rb", "bench/results.txt", "bert.gemspec", "ext/bert/c/decode.c", "ext/bert/c/extconf.rb", "lib/bert.rb", "lib/bert/bert.rb", "lib/bert/decode.rb", "lib/bert/decoder.rb", "lib/bert/encode.rb", "lib/bert/encoder.rb", "lib/bert/types.rb", "test/bert_test.rb", "test/decoder_test.rb", "test/encoder_test.rb", "test/test_helper.rb" ] s.homepage = %q{http://github.com/mojombo/bert} s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib", "ext"] s.rubygems_version = %q{1.3.5} s.summary = %q{BERT Serializiation for Ruby} s.test_files = [ "test/bert_test.rb", "test/decoder_test.rb", "test/encoder_test.rb", "test/test_helper.rb" ] if s.respond_to? :specification_version then current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION s.specification_version = 3 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, [">= 0"]) end end bert-1.1.6/Rakefile0000644000175000017500000000451612440122105013765 0ustar rbalintrbalintrequire 'rubygems' require 'rake' begin require 'jeweler' Jeweler::Tasks.new do |gem| gem.name = "bert" gem.summary = %Q{BERT Serializiation for Ruby} gem.description = %Q{BERT Serializiation for Ruby} gem.email = "tom@mojombo.com" gem.homepage = "http://github.com/mojombo/bert" gem.authors = ["Tom Preston-Werner"] gem.add_development_dependency("thoughtbot-shoulda") if ENV["JAVA"] gem.extensions = nil gem.platform = 'java' else gem.require_paths = ["lib", "ext"] gem.files.include("ext") gem.extensions << 'ext/bert/c/extconf.rb' end # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings end rescue LoadError puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" end require 'rake/testtask' Rake::TestTask.new(:runtests) do |test| test.libs << 'lib' << 'test' test.pattern = 'test/**/*_test.rb' test.verbose = true end task :make do Dir.chdir('ext/bert/c') { `ruby extconf.rb`; `make` } end task :clean do ['rm -f ext/bert/c/*.bundle', 'rm -f ext/bert/c/*.o'].each do |cmd| `#{cmd}` && puts(cmd) end end task :test => :check_dependencies do require 'fileutils' puts "\nCleaning extension build files and running all specs in native ruby mode..." ['rm -f ext/bert/c/*.bundle', 'rm -f ext/bert/c/*.o'].each do |cmd| `#{cmd}` && puts(cmd) end pid = fork do exec 'rake runtests' end Process.waitpid(pid) puts "\nRunning `make` to build extensions and rerunning decoder specs..." Dir.chdir('ext/bert/c') { `ruby extconf.rb`; `make` } pid = fork do exec 'rake runtests' end Process.waitpid(pid) end begin require 'rcov/rcovtask' Rcov::RcovTask.new do |test| test.libs << 'test' test.pattern = 'test/**/*_test.rb' test.verbose = true end rescue LoadError task :rcov do abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" end end task :default => :test require 'rake/rdoctask' Rake::RDocTask.new do |rdoc| if File.exist?('VERSION') version = File.read('VERSION') else version = "" end rdoc.rdoc_dir = 'rdoc' rdoc.title = "bert #{version}" rdoc.rdoc_files.include('README*') rdoc.rdoc_files.include('lib/**/*.rb') end task :console do exec('irb -I lib -rbert') endbert-1.1.6/test/0000755000175000017500000000000012440122105013271 5ustar rbalintrbalintbert-1.1.6/test/decoder_test.rb0000644000175000017500000000662012440122105016266 0ustar rbalintrbalintrequire 'test_helper' class DecoderTest < Test::Unit::TestCase BERT_NIL = [131,104,2,100,0,4,98,101,114,116,100,0,3,110,105,108].pack('c*') BERT_TRUE = [131,104,2,100,0,4,98,101,114,116,100,0,4,116,114,117,101].pack('c*') BERT_FALSE = [131,104,2,100,0,4,98,101,114,116,100,0,5,102,97,108,115,101].pack('c*') context "BERT Decoder complex type converter" do should "convert nil" do assert_equal nil, BERT::Decoder.decode(BERT_NIL) end should "convert nested nil" do bert = [131,108,0,0,0,2,104,2,100,0,4,98,101,114,116,100,0,3,110,105, 108,108,0,0,0,1,104,2,100,0,4,98,101,114,116,100,0,3,110,105, 108,106,106].pack('c*') assert_equal [nil, [nil]], BERT::Decoder.decode(bert) end should "convert hashes" do bert = [131,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116,108, 0,0,0,1,104,2,100,0,3,102,111,111,109,0,0,0,3,98,97,114, 106].pack('c*') after = {:foo => 'bar'} assert_equal after, BERT::Decoder.decode(bert) end should "convert empty hashes" do bert = [131,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116, 106].pack('c*') after = {} assert_equal after, BERT::Decoder.decode(bert) end should "convert nested hashes" do bert = [131,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116,108,0, 0,0,1,104,2,100,0,3,102,111,111,104,3,100,0,4,98,101,114,116, 100,0,4,100,105,99,116,108,0,0,0,1,104,2,100,0,3,98,97,122,109, 0,0,0,3,98,97,114,106,106].pack('c*') after = {:foo => {:baz => 'bar'}} assert_equal after, BERT::Decoder.decode(bert) end should "convert true" do assert_equal true, BERT::Decoder.decode(BERT_TRUE) end should "convert false" do assert_equal false, BERT::Decoder.decode(BERT_FALSE) end should "convert times" do bert = [131,104,5,100,0,4,98,101,114,116,100,0,4,116,105,109,101,98,0, 0,4,230,98,0,14,228,195,97,0].pack('c*') after = Time.at(1254976067) assert_equal after, BERT::Decoder.decode(bert) end should "convert regexen" do bert = [131,104,4,100,0,4,98,101,114,116,100,0,5,114,101,103,101,120, 109,0,0,0,7,94,99,40,97,41,116,36,108,0,0,0,2,100,0,8,99,97, 115,101,108,101,115,115,100,0,8,101,120,116,101,110,100,101, 100,106].pack('c*') after = /^c(a)t$/ix assert_equal after, BERT::Decoder.decode(bert) end should "leave other stuff alone" do bert = [131,108,0,0,0,3,97,1,99,50,46,48,48,48,48,48,48,48,48,48,48,48, 48,48,48,48,48,48,48,48,48,101,43,48,48,0,0,0,0,0,108,0,0,0,2, 100,0,3,102,111,111,109,0,0,0,3,98,97,114,106,106].pack('c*') after = [1, 2.0, [:foo, 'bar']] assert_equal after, BERT::Decoder.decode(bert) end should "handle bignums" do bert = [131,110,8,0,0,0,232,137,4,35,199,138].pack('c*') assert_equal 10_000_000_000_000_000_000, BERT::Decoder.decode(bert) end should "handle bytelists" do bert = [131,104,3,100,0,3,102,111,111,107,0,2,97,97,100,0,3,98,97,114].pack('c*') assert_equal t[:foo, [97, 97], :bar], BERT::Decoder.decode(bert) end should "handle massive binaries" do bert = [131,109,0,128,0,0].pack('c*') + ('a' * (8 * 1024 * 1024)) assert_equal (8 * 1024 * 1024), BERT::Decoder.decode(bert).size end end end bert-1.1.6/test/bert_test.rb0000644000175000017500000000467712440122105015627 0ustar rbalintrbalintrequire 'test_helper' class BertTest < Test::Unit::TestCase context "BERT" do setup do time = Time.at(1254976067) @ruby = t[:user, {:name => 'TPW'}, [/cat/i, 9.9], time, nil, true, false, :true, :false] @bert = "\203h\td\000\004userh\003d\000\004bertd\000\004dictl\000\000\000\001h\002d\000\004namem\000\000\000\003TPWjl\000\000\000\002h\004d\000\004bertd\000\005regexm\000\000\000\003catl\000\000\000\001d\000\bcaselessjc9.900000000000000e+00\000\000\000\000\000\000\000\000\000\000jh\005d\000\004bertd\000\004timeb\000\000\004\346b\000\016\344\303a\000h\002d\000\004bertd\000\003nilh\002d\000\004bertd\000\004trueh\002d\000\004bertd\000\005falsed\000\004trued\000\005false" @ebin = "<<131,104,9,100,0,4,117,115,101,114,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116,108,0,0,0,1,104,2,100,0,4,110,97,109,101,109,0,0,0,3,84,80,87,106,108,0,0,0,2,104,4,100,0,4,98,101,114,116,100,0,5,114,101,103,101,120,109,0,0,0,3,99,97,116,108,0,0,0,1,100,0,8,99,97,115,101,108,101,115,115,106,99,57,46,57,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,43,48,48,0,0,0,0,0,0,0,0,0,0,106,104,5,100,0,4,98,101,114,116,100,0,4,116,105,109,101,98,0,0,4,230,98,0,14,228,195,97,0,104,2,100,0,4,98,101,114,116,100,0,3,110,105,108,104,2,100,0,4,98,101,114,116,100,0,4,116,114,117,101,104,2,100,0,4,98,101,114,116,100,0,5,102,97,108,115,101,100,0,4,116,114,117,101,100,0,5,102,97,108,115,101>>" end should "encode" do assert_equal @bert, BERT.encode(@ruby) end should "decode" do assert_equal @ruby, BERT.decode(@bert) end should "ebin" do assert_equal @ebin, BERT.ebin(@bert) end should "do roundtrips" do dd = [] dd << 1 dd << 1.0 dd << :a dd << t[] dd << t[:a] dd << t[:a, :b] dd << t[t[:a, 1], t[:b, 2]] dd << [] dd << [:a] dd << [:a, 1] dd << [[:a, 1], [:b, 2]] dd << "a" dd << nil dd << true dd << false dd << {} dd << {:a => 1} dd << {:a => 1, :b => 2} dd << Time.now dd << /^c(a)t$/i dd << 178 dd << 256**256 - 1 dd << :true dd << :false dd << :nil dd.each do |d| assert_equal d, BERT.decode(BERT.encode(d)) end end # should "let me inspect it" do # puts # p @ruby # ruby2 = BERT.decode(@bert) # p ruby2 # bert2 = BERT.encode(ruby2) # ruby3 = BERT.decode(bert2) # p ruby3 # end end end bert-1.1.6/test/encoder_test.rb0000644000175000017500000000622012440122105016274 0ustar rbalintrbalint# encoding: utf-8 require 'test_helper' class EncoderTest < Test::Unit::TestCase context "BERT Encoder complex type converter" do should "convert nil" do assert_equal [:bert, :nil], BERT::Encoder.convert(nil) end should "convert nested nil" do before = [nil, [nil]] after = [[:bert, :nil], [[:bert, :nil]]] assert_equal after, BERT::Encoder.convert(before) end should "convert hashes" do before = {:foo => 'bar'} after = [:bert, :dict, [[:foo, 'bar']]] assert_equal after, BERT::Encoder.convert(before) end should "convert nested hashes" do before = {:foo => {:baz => 'bar'}} after = [:bert, :dict, [[:foo, [:bert, :dict, [[:baz, "bar"]]]]]] assert_equal after, BERT::Encoder.convert(before) end should "convert hash to tuple with array of tuples" do arr = BERT::Encoder.convert({:foo => 'bar'}) assert arr.is_a?(Array) assert arr[2].is_a?(Array) assert arr[2][0].is_a?(Array) end should "convert tuple to array" do arr = BERT::Encoder.convert(t[:foo, 2]) assert arr.is_a?(Array) end should "convert array to erl list" do list = BERT::Encoder.convert([1, 2]) assert list.is_a?(Array) end should "convert an array in a tuple" do arrtup = BERT::Encoder.convert(t[:foo, [1, 2]]) assert arrtup.is_a?(Array) assert arrtup[1].is_a?(Array) end should "convert true" do before = true after = [:bert, :true] assert_equal after, BERT::Encoder.convert(before) end should "convert false" do before = false after = [:bert, :false] assert_equal after, BERT::Encoder.convert(before) end should "convert times" do before = Time.at(1254976067) after = [:bert, :time, 1254, 976067, 0] assert_equal after, BERT::Encoder.convert(before) end should "convert regexen" do before = /^c(a)t$/ix after = [:bert, :regex, '^c(a)t$', [:caseless, :extended]] assert_equal after, BERT::Encoder.convert(before) end should "properly convert types" do ruby = t[:user, {:name => 'TPW'}, [/cat/i, 9.9], nil, true, false, :true, :false] cruby = BERT::Encoder.convert(ruby) assert cruby.instance_of?(BERT::Tuple) assert cruby[0].instance_of?(Symbol) assert cruby[1].instance_of?(BERT::Tuple) end should 'handle utf8 strings' do bert = [131, 109, 0, 0, 0, 5, 195, 169, 116, 195, 169].pack('C*') assert_equal bert, BERT::Encoder.encode("été") end should 'handle utf8 symbols' do bert = [131, 100, 0, 5, 195, 169, 116, 195, 169].pack('C*') assert_equal bert, BERT::Encoder.encode(:'été') end should "handle bignums" do bert = [131,110,8,0,0,0,232,137,4,35,199,138].pack('c*') assert_equal bert, BERT::Encoder.encode(10_000_000_000_000_000_000) bert = [131,110,8,1,0,0,232,137,4,35,199,138].pack('c*') assert_equal bert, BERT::Encoder.encode(-10_000_000_000_000_000_000) end should "leave other stuff alone" do before = [1, 2.0, [:foo, 'bar']] assert_equal before, BERT::Encoder.convert(before) end end end bert-1.1.6/test/test_helper.rb0000644000175000017500000000041712440122105016136 0ustar rbalintrbalintrequire 'rubygems' require 'test/unit' require 'shoulda' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'ext', 'bert', 'c')) load 'bert.rb' puts "Using #{BERT::Decode.impl} implementation."bert-1.1.6/.document0000644000175000017500000000007412440122105014132 0ustar rbalintrbalintREADME.rdoc lib/**/*.rb bin/* features/**/*.feature LICENSE bert-1.1.6/.gitignore0000644000175000017500000000013112440122105014275 0ustar rbalintrbalint*.sw? .DS_Store coverage rdoc pkg ext/bert/c/Makefile ext/bert/c/*.bundle ext/bert/c/*.o