erlang-etf 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c065a1c33b75cf6235b7911022718a22fa44204c
4
- data.tar.gz: 57fb1fc1ce89f1826ac5bcef122f1eb085430baa
3
+ metadata.gz: 320c12034e0d50d1f92aeeadcc0042a121f972de
4
+ data.tar.gz: c739e92472dbdef20ab555ddfb53b8404a7100d0
5
5
  SHA512:
6
- metadata.gz: 3979a241f92a3f08a19e81042c266381fc8e17837e08f7f7dd7be0d34d637bacc051d0fff6a808237c57386b23fed4cc13970247efb70054c7f3ba0355668f79
7
- data.tar.gz: 8947fad90e772020fff53c997d7c5240b73cb9cd298e4ab0f3f951d76aefc90ab193225a4241b4636f022c2031fecdb4d29f12bab2fdeb02e4003fc7795e6189
6
+ metadata.gz: d793115fb5b5bfe1694c0a23a0b0819435e206c91f8c55876cdbd0327e4bec433f0eafa9a4893e6e292832fb0a698e63c95db38c56d0a69857c3a87e4f2e34bd
7
+ data.tar.gz: 6d05ec4e5619e8eedc9d95dadb904b2b4d6aaef75c857572f5aa21cb9e2573ab37e88e09d31336ef7b6d46d14d7b29b36f5ed7e2be3dc0f826031503bc5bdd89
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -2,3 +2,5 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1.1
6
+ env: CI="travis"
data/Gemfile CHANGED
@@ -5,10 +5,16 @@ platforms :ruby do
5
5
  gem "pry"
6
6
  gem "pry-doc"
7
7
  gem "redcarpet"
8
- gem "simplecov", require: false
9
8
  gem "yard"
10
9
  end
11
10
  end
12
11
 
12
+ group :test do
13
+ gem "simplecov", require: false
14
+ if ENV['CI']
15
+ gem 'coveralls', require: false
16
+ end
17
+ end
18
+
13
19
  # Specify your gem's dependencies in erlang-etf.gemspec
14
20
  gemspec
data/README.md CHANGED
@@ -1,6 +1,4 @@
1
- # Erlang::ETF
2
-
3
- [![Build Status](https://travis-ci.org/potatosalad/erlang-etf.png)](https://travis-ci.org/potatosalad/erlang-etf)
1
+ # Erlang::ETF [![Build Status](https://travis-ci.org/potatosalad/erlang-etf.png)](https://travis-ci.org/potatosalad/erlang-etf) [![Coverage Status](https://coveralls.io/repos/potatosalad/erlang-etf/badge.png)](https://coveralls.io/r/potatosalad/erlang-etf)
4
2
 
5
3
  Erlang [External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html) (ETF) for Ruby.
6
4
 
@@ -33,6 +31,13 @@ Erlang.term_to_binary(:atom)
33
31
  # => "\x83s\x04atom"
34
32
  ```
35
33
 
34
+ Compression is optional and valid arguments are `false`, `true`, and integers `0-9`.
35
+
36
+ ```ruby
37
+ Erlang.term_to_binary([1] * 100, compressed: true)
38
+ # => "\x83P\x00\x00\x00\xCEx\x9C\xCBa``HId\x1C\x1E0\v\x00\xE85'\x83"
39
+ ```
40
+
36
41
  ### `Erlang.binary_to_term(binary)`
37
42
 
38
43
  ```ruby
@@ -12,9 +12,13 @@ module Erlang
12
12
 
13
13
  ERLANG_MAGIC_BYTE = 131.chr.freeze
14
14
 
15
- def encode(term, buffer = "")
15
+ def encode(term, buffer = "", options = {})
16
16
  buffer << ERLANG_MAGIC_BYTE
17
- term.__erlang_dump__(buffer)
17
+ if options[:compressed]
18
+ Compressed.new(term.__erlang_evolve__, options[:compressed]).serialize(buffer)
19
+ else
20
+ term.__erlang_dump__(buffer)
21
+ end
18
22
  end
19
23
 
20
24
  def decode(buffer)
@@ -33,8 +37,15 @@ module Erlang
33
37
  ETF.decode(buffer)
34
38
  end
35
39
 
36
- def self.term_to_binary(term, buffer = "")
37
- ETF.encode(term, buffer)
40
+ def self.term_to_binary(term, buffer_or_options = "", options = nil)
41
+ if buffer_or_options.kind_of?(::Hash)
42
+ buffer = options || ""
43
+ options = buffer_or_options
44
+ else
45
+ buffer = buffer_or_options
46
+ end
47
+ options ||= {}
48
+ ETF.encode(term, buffer, options)
38
49
  end
39
50
 
40
51
  end
@@ -0,0 +1,66 @@
1
+ require 'zlib'
2
+
3
+ module Erlang
4
+ module ETF
5
+
6
+ #
7
+ # 1 | 4 | N
8
+ # --- | ---------------- | -------------------
9
+ # 80 | UncompressedSize | Zlib-compressedData
10
+ #
11
+ # Uncompressed Size (unsigned 32 bit integer in big-endian
12
+ # byte order) is the size of the data before it was compressed.
13
+ # The compressed data has the following format when it has been expanded:
14
+ #
15
+ # 1 | Uncompressed Size
16
+ # ---- | -----------------
17
+ # Tag | Data
18
+ #
19
+ # (see [`External Term Format`] and [`term_to_binary/2`])
20
+ #
21
+ # [`External Term Format`]: http://erlang.org/doc/apps/erts/erl_ext_dist.html#overall_format
22
+ # [`term_to_binary/2`]: http://www.erlang.org/doc/man/erlang.html#term_to_binary-2
23
+ #
24
+ class Compressed
25
+ include Term
26
+
27
+ uint8 :tag, always: Terms::COMPRESSED
28
+
29
+ uint32be :uncompressed_size, always: -> { data.serialize.bytesize }
30
+
31
+ term :data
32
+
33
+ deserialize do |buffer|
34
+ uncompressed_size, = buffer.read(BYTES_32).unpack(UINT32BE_PACK)
35
+ compressed_data = buffer.read()
36
+ uncompressed_data = ::Zlib::Inflate.inflate(compressed_data)
37
+ if uncompressed_size == uncompressed_data.bytesize
38
+ deserialize_data(::StringIO.new(uncompressed_data))
39
+ else
40
+ raise ::Zlib::DataError, "UncompressedSize value did not match the size of the uncompressed data"
41
+ end
42
+ end
43
+
44
+ undef serialize_data
45
+ def serialize_data(buffer)
46
+ uncompressed_data = data.serialize
47
+ compressed_data = ::Zlib::Deflate.deflate(uncompressed_data, @level)
48
+ buffer << compressed_data
49
+ end
50
+
51
+ finalize
52
+
53
+ LEVEL_RANGE = (0..9).freeze
54
+ LEVEL_DEFAULT = 6.freeze
55
+
56
+ def initialize(data, level = LEVEL_DEFAULT)
57
+ @data = data
58
+ @level = LEVEL_RANGE.include?(level) ? level : LEVEL_DEFAULT
59
+ end
60
+
61
+ def __ruby_evolve__
62
+ data.__ruby_evolve__
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,7 +1,12 @@
1
1
  module Erlang
2
2
  module ETF
3
+
4
+ #
5
+ # See [`erts/emulator/beam/external.h`]
6
+ #
7
+ # [`erts/emulator/beam/external.h`]: https://github.com/erlang/otp/blob/master/erts/emulator/beam/external.h
8
+ #
3
9
  module Terms
4
- ATOM_CACHE_REF = 82.freeze # not implemented
5
10
  SMALL_INTEGER_EXT = 97.freeze
6
11
  INTEGER_EXT = 98.freeze
7
12
  FLOAT_EXT = 99.freeze
@@ -27,6 +32,14 @@ module Erlang
27
32
  ATOM_UTF8_EXT = 118.freeze
28
33
  SMALL_ATOM_UTF8_EXT = 119.freeze
29
34
  MAP_EXT = 116.freeze
35
+
36
+ DIST_HEADER = 68.freeze # not implemented
37
+ ATOM_CACHE_REF = 82.freeze # not implemented
38
+ ATOM_INTERNAL_REF2 = 73.freeze # not implemented
39
+ ATOM_INTERNAL_REF3 = 75.freeze # not implemented
40
+ BINARY_INTERNAL_REF = 74.freeze # not implemented
41
+ BIT_BINARY_INTERNAL_REF = 76.freeze # not implemented
42
+ COMPRESSED = 80.freeze
30
43
  end
31
44
  end
32
45
  end
@@ -38,6 +51,7 @@ require "erlang/etf/atom"
38
51
  require "erlang/etf/atom_utf8"
39
52
  require "erlang/etf/binary"
40
53
  require "erlang/etf/bit_binary"
54
+ require "erlang/etf/compressed"
41
55
  require "erlang/etf/export"
42
56
  require "erlang/etf/float"
43
57
  require "erlang/etf/fun"
@@ -64,7 +78,7 @@ module Erlang
64
78
  module ETF
65
79
  module Terms
66
80
  MAP = {}
67
- # MAP[ATOM_CACHE_REF] = undefined
81
+
68
82
  MAP[SMALL_INTEGER_EXT] = ETF::SmallInteger
69
83
  MAP[INTEGER_EXT] = ETF::Integer
70
84
  MAP[FLOAT_EXT] = ETF::Float
@@ -91,6 +105,16 @@ module Erlang
91
105
  MAP[SMALL_ATOM_UTF8_EXT] = ETF::SmallAtomUTF8
92
106
  MAP[MAP_EXT] = ETF::Map
93
107
 
108
+ # MAP[DIST_HEADER] = NotImplementedError
109
+ # MAP[ATOM_CACHE_REF] = NotImplementedError
110
+ # MAP[ATOM_INTERNAL_REF2] = NotImplementedError
111
+ # MAP[ATOM_INTERNAL_REF3] = NotImplementedError
112
+ # MAP[BINARY_INTERNAL_REF] = NotImplementedError
113
+ # MAP[BIT_BINARY_INTERNAL_REF] = NotImplementedError
114
+ MAP[COMPRESSED] = ETF::Compressed
115
+
116
+ MAP.freeze
117
+
94
118
  def self.deserialize(buffer)
95
119
  key, = buffer.read(1).unpack(::Binary::Protocol::UINT8_PACK)
96
120
  if MAP.key?(key)
@@ -1,5 +1,5 @@
1
1
  module Erlang
2
2
  module ETF
3
- VERSION = "1.1.0"
3
+ VERSION = "1.1.1"
4
4
  end
5
5
  end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Erlang::ETF::Compressed do
6
+ let(:term_class) { Erlang::ETF::Compressed }
7
+
8
+ describe '[class]' do
9
+ describe 'deserialize' do
10
+ let(:buffer) { StringIO.new(bytes.pack('C*')) }
11
+ subject { term_class.deserialize(buffer) }
12
+
13
+ context "valid zlib bytes" do
14
+ let(:bytes) { [0, 0, 0, 18, 120, 218, 203, 102, 224, 103, 68, 5, 0, 9, 0, 0, 138] }
15
+
16
+ its(:uncompressed_size) { should eq(18) }
17
+ its(:data) { should eq(::Erlang::ETF::String.new(([1] * 15).pack('C*').from_utf8_binary).tap { |s| s.len = 15 }) }
18
+ its(:__ruby_evolve__) { should eq(::Erlang::String.new(([1] * 15).pack('C*').from_utf8_binary)) }
19
+ end
20
+
21
+ context "invalid zlib bytes" do
22
+ subject { -> { term_class.deserialize(buffer) } }
23
+ let(:bytes) { [0, 0, 0, 18, 120, 218, 203, 102, 224, 103, 68, 5, 0, 9, 0, 0] }
24
+
25
+ it { should raise_error(::Zlib::BufError) }
26
+ end
27
+
28
+ context "valid zlib bytes, invalid uncompressed size" do
29
+ subject { -> { term_class.deserialize(buffer) } }
30
+ let(:bytes) { [0, 0, 0, 17, 120, 218, 203, 102, 224, 103, 68, 5, 0, 9, 0, 0, 138] }
31
+
32
+ it { should raise_error(::Zlib::DataError) }
33
+ end
34
+ end
35
+ end
36
+
37
+ end
@@ -21,6 +21,10 @@ describe Erlang do
21
21
  expect(term).to eq(Erlang.binary_to_term(Erlang.term_to_binary(term)))
22
22
  end
23
23
 
24
+ def roundtrip_compressed(term, compression)
25
+ expect(term).to eq(Erlang.binary_to_term(Erlang.term_to_binary(term, compressed: compression)))
26
+ end
27
+
24
28
  roundtrip_variables = [
25
29
  1,
26
30
  1.0,
@@ -78,4 +82,14 @@ describe Erlang do
78
82
  roundtrip(roundtrip_variable)
79
83
  end
80
84
  end
85
+
86
+ [false, true, *Erlang::ETF::Compressed::LEVEL_RANGE].each do |compression|
87
+ context "when compression is #{compression}" do
88
+ roundtrip_variables.each do |roundtrip_variable|
89
+ it "roundtrips #{roundtrip_variable.inspect}" do
90
+ roundtrip_compressed(roundtrip_variable, compression)
91
+ end
92
+ end
93
+ end
94
+ end
81
95
  end
@@ -1,7 +1,31 @@
1
- if ENV["COVERAGE"]
1
+ if ENV['CI']
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+ SimpleCov.start do
10
+ add_filter 'spec'
11
+ end
12
+ elsif ENV['COVERAGE']
2
13
  require 'simplecov'
3
14
  SimpleCov.start
4
15
  end
5
16
 
6
17
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
7
18
  require 'erlang/etf'
19
+
20
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
+ RSpec.configure do |config|
22
+ config.treat_symbols_as_metadata_keys_with_true_values = true
23
+ config.run_all_when_everything_filtered = true
24
+ config.filter_run :focus
25
+
26
+ # Run specs in random order to surface order dependencies. If you find an
27
+ # order dependency and want to debug it, you can fix the order by providing
28
+ # the seed, which is printed after each run.
29
+ # --seed 1234
30
+ config.order = 'random'
31
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erlang-etf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bennett
@@ -87,6 +87,7 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - .coveralls.yml
90
91
  - .gitignore
91
92
  - .rspec
92
93
  - .travis.yml
@@ -102,6 +103,7 @@ files:
102
103
  - lib/erlang/etf/bert.rb
103
104
  - lib/erlang/etf/binary.rb
104
105
  - lib/erlang/etf/bit_binary.rb
106
+ - lib/erlang/etf/compressed.rb
105
107
  - lib/erlang/etf/export.rb
106
108
  - lib/erlang/etf/extensions.rb
107
109
  - lib/erlang/etf/extensions/array.rb
@@ -151,6 +153,7 @@ files:
151
153
  - spec/erlang/etf/atom_utf8_spec.rb
152
154
  - spec/erlang/etf/binary_spec.rb
153
155
  - spec/erlang/etf/bit_binary_spec.rb
156
+ - spec/erlang/etf/compressed_spec.rb
154
157
  - spec/erlang/etf/export_spec.rb
155
158
  - spec/erlang/etf/extensions/array_spec.rb
156
159
  - spec/erlang/etf/extensions/big_decimal_spec.rb
@@ -226,6 +229,7 @@ test_files:
226
229
  - spec/erlang/etf/atom_utf8_spec.rb
227
230
  - spec/erlang/etf/binary_spec.rb
228
231
  - spec/erlang/etf/bit_binary_spec.rb
232
+ - spec/erlang/etf/compressed_spec.rb
229
233
  - spec/erlang/etf/export_spec.rb
230
234
  - spec/erlang/etf/extensions/array_spec.rb
231
235
  - spec/erlang/etf/extensions/big_decimal_spec.rb