erlang-terms 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.editorconfig +20 -0
- data/.gitignore +10 -18
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -3
- data/.yardopts +6 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +21 -1
- data/LICENSE.txt +1 -1
- data/README.md +95 -17
- data/Rakefile +8 -3
- data/erlang-terms.gemspec +14 -11
- data/lib/erlang-terms.rb +1 -0
- data/lib/erlang/associable.rb +98 -0
- data/lib/erlang/atom.rb +257 -0
- data/lib/erlang/binary.rb +425 -0
- data/lib/erlang/bitstring.rb +464 -0
- data/lib/erlang/cons.rb +122 -0
- data/lib/erlang/enumerable.rb +160 -0
- data/lib/erlang/error.rb +4 -0
- data/lib/erlang/export.rb +110 -12
- data/lib/erlang/float.rb +201 -0
- data/lib/erlang/function.rb +259 -0
- data/lib/erlang/immutable.rb +101 -0
- data/lib/erlang/list.rb +1685 -24
- data/lib/erlang/map.rb +935 -21
- data/lib/erlang/nil.rb +73 -10
- data/lib/erlang/pid.rb +120 -18
- data/lib/erlang/port.rb +123 -0
- data/lib/erlang/reference.rb +161 -0
- data/lib/erlang/string.rb +175 -3
- data/lib/erlang/term.rb +24 -0
- data/lib/erlang/terms.rb +324 -8
- data/lib/erlang/terms/version.rb +1 -1
- data/lib/erlang/trie.rb +364 -0
- data/lib/erlang/tuple.rb +1582 -14
- data/lib/erlang/undefined.rb +32 -0
- metadata +49 -71
- data/spec/erlang/export_spec.rb +0 -17
- data/spec/erlang/list_spec.rb +0 -39
- data/spec/erlang/map_spec.rb +0 -24
- data/spec/erlang/nil_spec.rb +0 -18
- data/spec/erlang/pid_spec.rb +0 -21
- data/spec/erlang/string_spec.rb +0 -11
- data/spec/erlang/terms_spec.rb +0 -7
- data/spec/erlang/tuple_spec.rb +0 -20
- data/spec/spec_helper.rb +0 -7
data/lib/erlang/atom.rb
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
module Erlang
|
2
|
+
# An `Atom` is a literal constant with a name.
|
3
|
+
#
|
4
|
+
# Symbols, Booleans (`true` and `false`), and `nil` are considered `Atom`s in Ruby.
|
5
|
+
#
|
6
|
+
# ### Creating Atoms
|
7
|
+
#
|
8
|
+
# Erlang::Atom["test"]
|
9
|
+
# # => :test
|
10
|
+
# Erlang::Atom[:Ω]
|
11
|
+
# # => :Ω
|
12
|
+
# Erlang::Atom[:Ω, utf8: true]
|
13
|
+
# # => Erlang::Atom["Ω", utf8: true]
|
14
|
+
# Erlang::Atom[true]
|
15
|
+
# # => true
|
16
|
+
# Erlang::Atom[false]
|
17
|
+
# # => false
|
18
|
+
# Erlang::Atom[nil]
|
19
|
+
# # => nil
|
20
|
+
#
|
21
|
+
#
|
22
|
+
class Atom
|
23
|
+
include Erlang::Term
|
24
|
+
include Erlang::Immutable
|
25
|
+
|
26
|
+
# Return the data for this `Atom`
|
27
|
+
# @return [::String]
|
28
|
+
attr_reader :data
|
29
|
+
|
30
|
+
# Return the utf8 flag for this `Atom`
|
31
|
+
# @return [::Boolean]
|
32
|
+
attr_reader :utf8
|
33
|
+
|
34
|
+
class << self
|
35
|
+
# Create a new `Atom` populated with the given `data` and `utf8` flag.
|
36
|
+
# @param data [::String, Symbol, ::Enumerable, Integer] The content of the `Atom`
|
37
|
+
# @param utf8 [Boolean] Whether the `Atom` should be considered UTF-8 or not
|
38
|
+
# @return [Atom]
|
39
|
+
# @raise [ArgumentError] if `data` cannot be coerced to be a `::String`
|
40
|
+
def [](*data, utf8: false)
|
41
|
+
return EmptyAtom if data.empty?
|
42
|
+
if data.size == 1
|
43
|
+
return data[0] if data[0].is_a?(Erlang::Atom)
|
44
|
+
return FalseAtom if data[0] == false
|
45
|
+
return NilAtom if data[0] == nil
|
46
|
+
return TrueAtom if data[0] == true
|
47
|
+
if data[0].is_a?(::String)
|
48
|
+
data = data[0]
|
49
|
+
elsif data[0].is_a?(::Symbol)
|
50
|
+
data = data[0].to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
unless data.is_a?(::String)
|
54
|
+
data = Erlang.iolist_to_binary(data).data
|
55
|
+
end
|
56
|
+
return FalseAtom if data == "false"
|
57
|
+
return NilAtom if data == "nil"
|
58
|
+
return TrueAtom if data == "true"
|
59
|
+
return new(data, utf8)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return an empty `Atom`. If used on a subclass, returns an empty instance
|
63
|
+
# of that class.
|
64
|
+
#
|
65
|
+
# @return [Atom]
|
66
|
+
def empty
|
67
|
+
return @empty ||= self.new
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return a false `Atom`.
|
71
|
+
#
|
72
|
+
# @return [Atom]
|
73
|
+
def false
|
74
|
+
return @false ||= self.new("false")
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return a nil `Atom`.
|
78
|
+
#
|
79
|
+
# @return [Atom]
|
80
|
+
def nil
|
81
|
+
return @nil ||= self.new("nil")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return a true `Atom`.
|
85
|
+
#
|
86
|
+
# @return [Atom]
|
87
|
+
def true
|
88
|
+
return @true ||= self.new("true")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Compares `a` and `b` and returns whether they are less than,
|
92
|
+
# equal to, or greater than each other.
|
93
|
+
#
|
94
|
+
# @param a [Atom] The left argument
|
95
|
+
# @param b [Atom] The right argument
|
96
|
+
# @return [-1, 0, 1]
|
97
|
+
# @raise [ArgumentError] if `a` or `b` is not an `Atom`
|
98
|
+
def compare(a, b)
|
99
|
+
raise ArgumentError, "'a' must be of Erlang::Atom type" unless a.kind_of?(Erlang::Atom)
|
100
|
+
raise ArgumentError, "'b' must be of Erlang::Atom type" unless b.kind_of?(Erlang::Atom)
|
101
|
+
return a.data <=> b.data
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @private
|
106
|
+
def initialize(data = ::String.new.freeze, utf8 = false)
|
107
|
+
raise ArgumentError, 'data must be a String' if not data.is_a?(::String)
|
108
|
+
@valid_utf8, data = Erlang::Terms.utf8_encoding(data)
|
109
|
+
@printable = Erlang::Terms.printable?(data)
|
110
|
+
data = Erlang::Terms.binary_encoding(data) if not @printable
|
111
|
+
@data = data.freeze
|
112
|
+
@utf8 = !!utf8
|
113
|
+
if @data == "false"
|
114
|
+
@internal = false
|
115
|
+
elsif @data == "nil"
|
116
|
+
@internal = nil
|
117
|
+
elsif @data == "true"
|
118
|
+
@internal = true
|
119
|
+
else
|
120
|
+
@internal = @data.intern
|
121
|
+
end
|
122
|
+
valid_internal = false
|
123
|
+
if @utf8 == false and @valid_utf8 and @printable
|
124
|
+
begin
|
125
|
+
if @internal == eval(@internal.inspect)
|
126
|
+
valid_internal = true
|
127
|
+
end
|
128
|
+
rescue SyntaxError
|
129
|
+
valid_internal = false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
@valid_internal = valid_internal
|
133
|
+
end
|
134
|
+
|
135
|
+
# @private
|
136
|
+
def hash
|
137
|
+
return @internal.hash
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return true if `other` has the same type and contents as this `Atom`.
|
141
|
+
#
|
142
|
+
# @param other [Object] The object to compare with
|
143
|
+
# @return [Boolean]
|
144
|
+
def eql?(other)
|
145
|
+
return true if other.equal?(self)
|
146
|
+
if instance_of?(other.class)
|
147
|
+
return !!(@utf8 == other.utf8 && self.hash == other.hash)
|
148
|
+
else
|
149
|
+
return !!(Erlang.compare(other, self) == 0)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
alias :== :eql?
|
153
|
+
|
154
|
+
# Returns the length of this `Atom`.
|
155
|
+
#
|
156
|
+
# @return [Integer]
|
157
|
+
def length
|
158
|
+
return @data.bytesize
|
159
|
+
end
|
160
|
+
alias :size :length
|
161
|
+
|
162
|
+
# Return the contents of this `Atom` as a Erlang-readable `::String`.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# Erlang::Atom[:test].erlang_inspect
|
166
|
+
# # => "'test'"
|
167
|
+
# # Pass `utf8: true` for a UTF-8 version of the Atom
|
168
|
+
# Erlang::Atom[:Ω].erlang_inspect
|
169
|
+
# # => "'\\xCE\\xA9'"
|
170
|
+
# Erlang::Atom[:Ω, utf8: true].erlang_inspect
|
171
|
+
# # => "'Ω'"
|
172
|
+
#
|
173
|
+
# @return [::String]
|
174
|
+
def erlang_inspect(raw = false)
|
175
|
+
if @utf8
|
176
|
+
result = '\''
|
177
|
+
result << (data.inspect[1..-2].gsub('\''){"\\'"})
|
178
|
+
result << '\''
|
179
|
+
return result
|
180
|
+
else
|
181
|
+
result = '\''
|
182
|
+
data = @valid_utf8 ? Erlang::Terms.binary_encoding(@data) : @data
|
183
|
+
result << (data.inspect[1..-2].gsub('\''){"\\'"})
|
184
|
+
result << '\''
|
185
|
+
return result
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# @return [::String] the nicely formatted version of the `Atom`
|
190
|
+
def inspect
|
191
|
+
if @valid_internal
|
192
|
+
return @internal.inspect
|
193
|
+
elsif @utf8 == true
|
194
|
+
return "Erlang::Atom[#{@data.inspect}, utf8: true]"
|
195
|
+
else
|
196
|
+
return "Erlang::Atom[#{@data.inspect}]"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# @return [self] the `Atom` version of the `Atom`
|
201
|
+
def to_atom
|
202
|
+
return self
|
203
|
+
end
|
204
|
+
|
205
|
+
# @return [Binary] the `Binary` version of the `Atom`
|
206
|
+
def to_binary
|
207
|
+
return Erlang::Binary[@data]
|
208
|
+
end
|
209
|
+
|
210
|
+
# @return [List] the `List` version of the `Atom`
|
211
|
+
def to_list
|
212
|
+
return Erlang::List.from_enum(@data.bytes)
|
213
|
+
end
|
214
|
+
|
215
|
+
# @return [self] the `String` version of the `Atom`
|
216
|
+
def to_string
|
217
|
+
return Erlang::String[@data]
|
218
|
+
end
|
219
|
+
|
220
|
+
# @return [::String] the string version of the `Atom`
|
221
|
+
def to_s
|
222
|
+
return @data
|
223
|
+
end
|
224
|
+
alias :to_str :to_s
|
225
|
+
|
226
|
+
# @return [::Array]
|
227
|
+
# @private
|
228
|
+
def marshal_dump
|
229
|
+
return [@internal, @utf8]
|
230
|
+
end
|
231
|
+
|
232
|
+
# @private
|
233
|
+
def marshal_load(args)
|
234
|
+
internal, utf8 = args
|
235
|
+
atom = self.class[internal, utf8: utf8]
|
236
|
+
initialize(atom.data, atom.utf8)
|
237
|
+
__send__(:immutable!)
|
238
|
+
return self
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# The canonical empty `Atom`. Returned by `Atom[]` when
|
243
|
+
# invoked with no arguments; also returned by `Atom.empty`. Prefer using this
|
244
|
+
# one rather than creating many empty atoms using `Atom.new`.
|
245
|
+
#
|
246
|
+
# @private
|
247
|
+
EmptyAtom = Erlang::Atom.empty
|
248
|
+
|
249
|
+
# @private
|
250
|
+
FalseAtom = Erlang::Atom.false
|
251
|
+
|
252
|
+
# @private
|
253
|
+
NilAtom = Erlang::Atom.nil
|
254
|
+
|
255
|
+
# @private
|
256
|
+
TrueAtom = Erlang::Atom.true
|
257
|
+
end
|
@@ -0,0 +1,425 @@
|
|
1
|
+
module Erlang
|
2
|
+
# A `Binary` is a series of bytes.
|
3
|
+
#
|
4
|
+
# ### Creating Binaries
|
5
|
+
#
|
6
|
+
# Erlang::Binary["test"]
|
7
|
+
# # => Erlang::Binary["test"]
|
8
|
+
#
|
9
|
+
class Binary
|
10
|
+
include Erlang::Term
|
11
|
+
include Erlang::Immutable
|
12
|
+
|
13
|
+
# Return the data for this `Erlang::Binary`
|
14
|
+
# @return [::String]
|
15
|
+
attr_reader :data
|
16
|
+
|
17
|
+
# Return the bits for this `Erlang::Binary`
|
18
|
+
# @return [::Integer]
|
19
|
+
# @!parse attr_reader :bits
|
20
|
+
def bits
|
21
|
+
return 8
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Create a new `Binary` populated with the given `data`.
|
26
|
+
# @param data [::String, Symbol, ::Enumerable, Integer] The content of the `Binary`
|
27
|
+
# @return [Binary]
|
28
|
+
# @raise [ArgumentError] if `data` cannot be coerced to be a `::String`
|
29
|
+
def [](*data)
|
30
|
+
return Erlang.iolist_to_binary(data)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return an empty `Binary`. If used on a subclass, returns an empty instance
|
34
|
+
# of that class.
|
35
|
+
#
|
36
|
+
# @return [Binary]
|
37
|
+
def empty
|
38
|
+
return @empty ||= self.new
|
39
|
+
end
|
40
|
+
|
41
|
+
# Compares `a` and `b` and returns whether they are less than,
|
42
|
+
# equal to, or greater than each other.
|
43
|
+
#
|
44
|
+
# @param a [Binary] The left argument
|
45
|
+
# @param b [Binary] The right argument
|
46
|
+
# @return [-1, 0, 1]
|
47
|
+
# @raise [ArgumentError] if `a` or `b` is not a `Binary`
|
48
|
+
def compare(a, b)
|
49
|
+
return Erlang::Bitstring.compare(a, b)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Concatenates list of `Binary` or `Bitstring` items into a single `Binary` or `Bitstring`.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# Erlang::Binary.concat(Erlang::Bitstring[1, bits: 2], Erlang::Binary[255])
|
56
|
+
# # => Erlang::Bitstring[127, 3, bits: 2]
|
57
|
+
#
|
58
|
+
# @param iodata [Binary, Bitstring] The list of bitstrings
|
59
|
+
# @return [Binary, Bitstring]
|
60
|
+
def concat(*iodata)
|
61
|
+
return iodata.reduce(Erlang::EmptyBinary) { |acc, item| acc.concat(item) }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns an unsigned `Integer` which is the `endianness` based version of the `subject`.
|
65
|
+
#
|
66
|
+
# @param subject [::String, Binary] The string to decode
|
67
|
+
# @param endianness [:big, :little] The endianness of the subject
|
68
|
+
# @return [Integer]
|
69
|
+
# @raise [ArgumentError] if `subject` is not a string or `endianness` is not `:big` or `:little`
|
70
|
+
def decode_unsigned(subject, endianness = :big)
|
71
|
+
subject = subject.to_s if subject.kind_of?(Erlang::Binary)
|
72
|
+
raise ArgumentError, 'subject must be a String' if not subject.is_a?(::String)
|
73
|
+
case endianness
|
74
|
+
when :big, :little
|
75
|
+
bits = 0
|
76
|
+
bytes = subject.unpack(Erlang::Terms::UINT8_SPLAT)
|
77
|
+
bytes = bytes.reverse if endianness == :big
|
78
|
+
return bytes.inject(0) do |unsigned, n|
|
79
|
+
unsigned = unsigned + (n << bits)
|
80
|
+
bits += 8
|
81
|
+
next unsigned
|
82
|
+
end
|
83
|
+
else
|
84
|
+
raise ArgumentError, 'endianness must be :big or :little'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a `::String` which is the `endianness` based version of the unsigned `subject`.
|
89
|
+
#
|
90
|
+
# @param subject [Integer] The unsigned integer to encode
|
91
|
+
# @param endianness [:big, :little] The endianness of the subject
|
92
|
+
# @return [::String]
|
93
|
+
# @raise [ArgumentError] if `subject` is not an integer or `endianness` is not `:big` or `:little`
|
94
|
+
def encode_unsigned(unsigned, endianness = :big)
|
95
|
+
raise ArgumentError, 'unsigned must be a non-negative Integer' if not unsigned.is_a?(::Integer) or unsigned < 0
|
96
|
+
case endianness
|
97
|
+
when :big, :little
|
98
|
+
n = unsigned
|
99
|
+
bytes = []
|
100
|
+
loop do
|
101
|
+
bytes << (n & 255)
|
102
|
+
break if (n >>= 8) == 0
|
103
|
+
end
|
104
|
+
bytes = bytes.reverse if endianness == :big
|
105
|
+
return bytes.pack(Erlang::Terms::UINT8_SPLAT)
|
106
|
+
else
|
107
|
+
raise ArgumentError, 'endianness must be :big or :little'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @private
|
113
|
+
def initialize(data = nil)
|
114
|
+
data = ::String.new if data.nil?
|
115
|
+
data = data.data if data.is_a?(Erlang::Binary)
|
116
|
+
raise ArgumentError, 'data must be a String' if not data.is_a?(::String)
|
117
|
+
_, data = Erlang::Terms.utf8_encoding(data)
|
118
|
+
@printable = Erlang::Terms.printable?(data)
|
119
|
+
data = Erlang::Terms.binary_encoding(data) if not @printable
|
120
|
+
@data = data.freeze
|
121
|
+
end
|
122
|
+
|
123
|
+
# @private
|
124
|
+
def hash
|
125
|
+
return @data.hash
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns the byte at the provided `position`.
|
129
|
+
#
|
130
|
+
# @param position [Integer] The position of the byte
|
131
|
+
# @return [Integer]
|
132
|
+
# @raise [ArgumentError] if `position` is not an `Integer`
|
133
|
+
def at(position)
|
134
|
+
raise ArgumentError, 'position must be an Integer' if not position.is_a?(::Integer)
|
135
|
+
return @data.getbyte(position)
|
136
|
+
end
|
137
|
+
alias :[] :at
|
138
|
+
|
139
|
+
# @return [Integer] the number of bits in this `Binary`
|
140
|
+
def bitsize
|
141
|
+
return (bytesize * bits)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return specific objects from the `Binary`. All overloads return `nil` if
|
145
|
+
# the starting index is out of range.
|
146
|
+
#
|
147
|
+
# @overload bitslice(index)
|
148
|
+
# Returns a single bit at the given `index`. If `index` is negative,
|
149
|
+
# count backwards from the end.
|
150
|
+
#
|
151
|
+
# @param index [Integer] The index to retrieve. May be negative.
|
152
|
+
# @return [Integer, nil]
|
153
|
+
# @example
|
154
|
+
# b = Erlang::Binary[1]
|
155
|
+
# b.bitslice(0) # => 0
|
156
|
+
# b.bitslice(7) # => 1
|
157
|
+
# b.bitslice(-1) # => 1
|
158
|
+
# b.bitslice(8) # => nil
|
159
|
+
#
|
160
|
+
# @overload bitslice(index, length)
|
161
|
+
# Return a bitstring starting at `index` and continuing for `length`
|
162
|
+
# bits or until the end of the `Binary`, whichever occurs first.
|
163
|
+
#
|
164
|
+
# @param start [Integer] The index to start retrieving bits from. May be
|
165
|
+
# negative.
|
166
|
+
# @param length [Integer] The number of bits to retrieve.
|
167
|
+
# @return [Bitstring, Binary]
|
168
|
+
# @example
|
169
|
+
# b = Erlang::Binary[117]
|
170
|
+
# b.bitslice(0, 3) # => Erlang::Bitstring[3, bits: 3]
|
171
|
+
# b.bitslice(3, 5) # => Erlang::Bitstring[21, bits: 5]
|
172
|
+
# b.bitslice(9, 1) # => nil
|
173
|
+
#
|
174
|
+
# @overload bitslice(index..end)
|
175
|
+
# Return a bitstring starting at `index` and continuing to index
|
176
|
+
# `end` or the end of the `Binary`, whichever occurs first.
|
177
|
+
#
|
178
|
+
# @param range [Range] The range of bits to retrieve.
|
179
|
+
# @return [Bitstring, Binary]
|
180
|
+
# @example
|
181
|
+
# b = Erlang::Binary[117]
|
182
|
+
# b.bitslice(0...3) # => Erlang::Bitstring[3, bits: 3]
|
183
|
+
# b.bitslice(3...8) # => Erlang::Bitstring[21, bits: 5]
|
184
|
+
# b.bitslice(9..-1) # => nil
|
185
|
+
#
|
186
|
+
# @see Erlang::Bitstring#bitslice
|
187
|
+
def bitslice(*args)
|
188
|
+
return Erlang::Bitstring.new(@data, 8).bitslice(*args)
|
189
|
+
end
|
190
|
+
|
191
|
+
# @return [Integer] the number of bytes in this `Binary`
|
192
|
+
def bytesize
|
193
|
+
return @data.bytesize
|
194
|
+
end
|
195
|
+
alias :size :bytesize
|
196
|
+
|
197
|
+
# Concatenates list of `Binary` or `Bitstring` items into a single `Binary` or `Bitstring`.
|
198
|
+
#
|
199
|
+
# @example
|
200
|
+
# Erlang::Binary["a"].concat(Erlang::Bitstring[3, bits: 3]).concat(Erlang::Bitstring[2, bits: 5])
|
201
|
+
# # => "ab"
|
202
|
+
#
|
203
|
+
# @param iodata [Binary, Bitstring] The list of bitstrings
|
204
|
+
# @return [Binary, Bitstring]
|
205
|
+
def concat(*other)
|
206
|
+
if other.size == 1 and (other[0].kind_of?(Erlang::Binary) or other[0].kind_of?(Erlang::Bitstring))
|
207
|
+
other = other[0]
|
208
|
+
else
|
209
|
+
other = Erlang::Binary[*other]
|
210
|
+
end
|
211
|
+
return other if empty?
|
212
|
+
return self if other.empty?
|
213
|
+
if other.kind_of?(Erlang::Bitstring) and other.bits != 8
|
214
|
+
return Erlang::Bitstring[@data, other.data, bits: other.bits]
|
215
|
+
else
|
216
|
+
return Erlang::Binary[@data, other.data]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
alias :+ :concat
|
220
|
+
|
221
|
+
# Returns a new `Binary` containing `n` copies of itself. `n` must be greater than or equal to 0.
|
222
|
+
# @param n [Integer] The number of copies
|
223
|
+
# @return [Binary]
|
224
|
+
# @raise [ArgumentError] if `n` is less than 0
|
225
|
+
def copy(n = 1)
|
226
|
+
raise ArgumentError, 'n must be a non-negative Integer' if not n.is_a?(::Integer) or n < 0
|
227
|
+
return self if n == 1
|
228
|
+
return Erlang::Binary[(@data * n)]
|
229
|
+
end
|
230
|
+
|
231
|
+
# Returns an unsigned `Integer` which is the `endianness` based version of this `Binary`.
|
232
|
+
# @param endianness [:big, :little] The endianness of this `Binary`
|
233
|
+
# @return [Integer]
|
234
|
+
# @see Erlang::Binary.decode_unsigned
|
235
|
+
def decode_unsigned(endianness = :big)
|
236
|
+
return Erlang::Binary.decode_unsigned(@data, endianness)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Call the given block once for each bit in the `Binary`, passing each
|
240
|
+
# bit from first to last successively to the block. If no block is given,
|
241
|
+
# returns an `Enumerator`.
|
242
|
+
#
|
243
|
+
# @return [self]
|
244
|
+
# @yield [Integer]
|
245
|
+
def each_bit
|
246
|
+
return enum_for(:each_bit) unless block_given?
|
247
|
+
index = 0
|
248
|
+
bitsize = self.bitsize
|
249
|
+
@data.each_byte do |byte|
|
250
|
+
loop do
|
251
|
+
break if index == bitsize
|
252
|
+
bit = (byte >> (7 - (index & 7))) & 1
|
253
|
+
yield bit
|
254
|
+
index += 1
|
255
|
+
break if (index & 7) == 0
|
256
|
+
end
|
257
|
+
end
|
258
|
+
return self
|
259
|
+
end
|
260
|
+
|
261
|
+
# Split the bits in this `Binary` in groups of `number` bits, and yield
|
262
|
+
# each group to the block (as a `List`). If no block is given, returns
|
263
|
+
# an `Enumerator`.
|
264
|
+
#
|
265
|
+
# @example
|
266
|
+
# b = Erlang::Binary[117]
|
267
|
+
# b.each_bitslice(4).to_a # => [Erlang::Bitstring[7, bits: 4], Erlang::Bitstring[5, bits: 4]]
|
268
|
+
#
|
269
|
+
# @return [self, Enumerator]
|
270
|
+
# @yield [Binary, Bitstring] Once for each bitstring.
|
271
|
+
# @see Erlang::Bitstring#each_bitslice
|
272
|
+
def each_bitslice(number, &block)
|
273
|
+
return enum_for(:each_bitslice, number) unless block_given?
|
274
|
+
bitstring = Erlang::Bitstring.new(@data, 8)
|
275
|
+
bitstring.each_bitslice(number, &block)
|
276
|
+
return self
|
277
|
+
end
|
278
|
+
|
279
|
+
# Call the given block once for each byte in the `Binary`, passing each
|
280
|
+
# byte from first to last successively to the block. If no block is given,
|
281
|
+
# returns an `Enumerator`.
|
282
|
+
#
|
283
|
+
# @return [self]
|
284
|
+
# @yield [Integer]
|
285
|
+
def each_byte(&block)
|
286
|
+
return enum_for(:each_byte) unless block_given?
|
287
|
+
@data.each_byte(&block)
|
288
|
+
return self
|
289
|
+
end
|
290
|
+
|
291
|
+
# Returns true if this `Binary` is empty.
|
292
|
+
#
|
293
|
+
# @return [Boolean]
|
294
|
+
def empty?
|
295
|
+
return @data.empty?
|
296
|
+
end
|
297
|
+
|
298
|
+
# Return true if `other` has the same type and contents as this `Binary`.
|
299
|
+
#
|
300
|
+
# @param other [Object] The object to compare with
|
301
|
+
# @return [Boolean]
|
302
|
+
def eql?(other)
|
303
|
+
return true if other.equal?(self)
|
304
|
+
if instance_of?(other.class)
|
305
|
+
return @data.eql?(other.data)
|
306
|
+
elsif other.kind_of?(Erlang::Bitstring)
|
307
|
+
return !!(Erlang::Bitstring.compare(other, self) == 0)
|
308
|
+
else
|
309
|
+
return !!(Erlang.compare(other, self) == 0)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
alias :== :eql?
|
313
|
+
|
314
|
+
# Returns the first byte of this `Binary`.
|
315
|
+
# @return [Integer]
|
316
|
+
# @raise [NotImplementedError] if this `Binary` is empty
|
317
|
+
def first
|
318
|
+
raise NotImplementedError if empty?
|
319
|
+
return at(0)
|
320
|
+
end
|
321
|
+
|
322
|
+
# Returns the last byte of this `Binary`.
|
323
|
+
# @return [Integer]
|
324
|
+
# @raise [NotImplementedError] if this `Binary` is empty
|
325
|
+
def last
|
326
|
+
raise NotImplementedError if empty?
|
327
|
+
return at(-1)
|
328
|
+
end
|
329
|
+
|
330
|
+
# Returns the section of this `Binary` starting at `position` of `length`.
|
331
|
+
#
|
332
|
+
# @param position [Integer] The starting position
|
333
|
+
# @param length [Integer] The non-negative length
|
334
|
+
# @return [Binary]
|
335
|
+
# @raise [ArgumentError] if `position` is not an `Integer` or `length` is not a non-negative `Integer`
|
336
|
+
def part(position, length)
|
337
|
+
raise ArgumentError, 'position must be an Integer' if not position.is_a?(::Integer)
|
338
|
+
raise ArgumentError, 'length must be a non-negative Integer' if not length.is_a?(::Integer) or length < 0
|
339
|
+
return Erlang::Binary[@data.byteslice(position, length)]
|
340
|
+
end
|
341
|
+
|
342
|
+
# Return the contents of this `Binary` as a Erlang-readable `::String`.
|
343
|
+
#
|
344
|
+
# @example
|
345
|
+
# Erlang::Binary["test"].erlang_inspect
|
346
|
+
# # => "<<\"test\"/utf8>>"
|
347
|
+
# # Pass `raw` as `true` for the decimal version
|
348
|
+
# Erlang::Binary["test"].erlang_inspect(true)
|
349
|
+
# # => "<<116,101,115,116>>"
|
350
|
+
#
|
351
|
+
# @return [::String]
|
352
|
+
def erlang_inspect(raw = false)
|
353
|
+
result = '<<'
|
354
|
+
if raw == false and @printable
|
355
|
+
result << @data.inspect
|
356
|
+
result << '/utf8'
|
357
|
+
else
|
358
|
+
result << @data.bytes.join(',')
|
359
|
+
end
|
360
|
+
result << '>>'
|
361
|
+
return result
|
362
|
+
end
|
363
|
+
|
364
|
+
# @return [::String] the nicely formatted version of the `Binary`
|
365
|
+
def inspect
|
366
|
+
return @data.inspect
|
367
|
+
end
|
368
|
+
|
369
|
+
# @return [Atom] the `Atom` version of the `Binary`
|
370
|
+
def to_atom
|
371
|
+
return Erlang::Atom[@data]
|
372
|
+
end
|
373
|
+
|
374
|
+
# @return [self] the `Binary` version of the `Binary`
|
375
|
+
def to_binary
|
376
|
+
return self
|
377
|
+
end
|
378
|
+
|
379
|
+
# @param bits [Integer] The number of bits to keep for the last byte
|
380
|
+
# @return [Bitstring, self] the `Bitstring` version of the `Binary`
|
381
|
+
def to_bitstring(bits = 8)
|
382
|
+
if bits == 8
|
383
|
+
return self
|
384
|
+
else
|
385
|
+
return Erlang::Bitstring[@data, bits: bits]
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# @return [List] the `List` version of the `Binary`
|
390
|
+
def to_list
|
391
|
+
return Erlang::List.from_enum(@data.bytes)
|
392
|
+
end
|
393
|
+
|
394
|
+
# @return [String] the `String` version of the `Binary`
|
395
|
+
def to_string
|
396
|
+
return Erlang::String[@data]
|
397
|
+
end
|
398
|
+
|
399
|
+
# @return [::String] the string version of the `Binary`
|
400
|
+
def to_s
|
401
|
+
return @data
|
402
|
+
end
|
403
|
+
alias :to_str :to_s
|
404
|
+
|
405
|
+
# @return [::String]
|
406
|
+
# @private
|
407
|
+
def marshal_dump
|
408
|
+
return @data
|
409
|
+
end
|
410
|
+
|
411
|
+
# @private
|
412
|
+
def marshal_load(data)
|
413
|
+
initialize(data)
|
414
|
+
__send__(:immutable!)
|
415
|
+
return self
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# The canonical empty `Binary`. Returned by `Binary[]` when
|
420
|
+
# invoked with no arguments; also returned by `Binary.empty`. Prefer using this
|
421
|
+
# one rather than creating many empty binaries using `Binary.new`.
|
422
|
+
#
|
423
|
+
# @private
|
424
|
+
EmptyBinary = Erlang::Binary.empty
|
425
|
+
end
|