erlang-terms 1.1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.editorconfig +20 -0
  4. data/.gitignore +10 -18
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +15 -3
  8. data/.yardopts +6 -0
  9. data/CHANGELOG.md +9 -0
  10. data/Gemfile +21 -1
  11. data/LICENSE.txt +1 -1
  12. data/README.md +95 -17
  13. data/Rakefile +8 -3
  14. data/erlang-terms.gemspec +14 -11
  15. data/lib/erlang-terms.rb +1 -0
  16. data/lib/erlang/associable.rb +98 -0
  17. data/lib/erlang/atom.rb +257 -0
  18. data/lib/erlang/binary.rb +425 -0
  19. data/lib/erlang/bitstring.rb +464 -0
  20. data/lib/erlang/cons.rb +122 -0
  21. data/lib/erlang/enumerable.rb +160 -0
  22. data/lib/erlang/error.rb +4 -0
  23. data/lib/erlang/export.rb +110 -12
  24. data/lib/erlang/float.rb +201 -0
  25. data/lib/erlang/function.rb +259 -0
  26. data/lib/erlang/immutable.rb +101 -0
  27. data/lib/erlang/list.rb +1685 -24
  28. data/lib/erlang/map.rb +935 -21
  29. data/lib/erlang/nil.rb +73 -10
  30. data/lib/erlang/pid.rb +120 -18
  31. data/lib/erlang/port.rb +123 -0
  32. data/lib/erlang/reference.rb +161 -0
  33. data/lib/erlang/string.rb +175 -3
  34. data/lib/erlang/term.rb +24 -0
  35. data/lib/erlang/terms.rb +324 -8
  36. data/lib/erlang/terms/version.rb +1 -1
  37. data/lib/erlang/trie.rb +364 -0
  38. data/lib/erlang/tuple.rb +1582 -14
  39. data/lib/erlang/undefined.rb +32 -0
  40. metadata +49 -71
  41. data/spec/erlang/export_spec.rb +0 -17
  42. data/spec/erlang/list_spec.rb +0 -39
  43. data/spec/erlang/map_spec.rb +0 -24
  44. data/spec/erlang/nil_spec.rb +0 -18
  45. data/spec/erlang/pid_spec.rb +0 -21
  46. data/spec/erlang/string_spec.rb +0 -11
  47. data/spec/erlang/terms_spec.rb +0 -7
  48. data/spec/erlang/tuple_spec.rb +0 -20
  49. data/spec/spec_helper.rb +0 -7
@@ -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