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
@@ -0,0 +1,464 @@
|
|
1
|
+
module Erlang
|
2
|
+
# A `Bitstring` is a series of bits.
|
3
|
+
#
|
4
|
+
# ### Creating Bitstrings
|
5
|
+
#
|
6
|
+
# Erlang::Bitstirng["test", bits: 7]
|
7
|
+
# # => Erlang::Bitstring[116, 101, 115, 4, bits: 3]
|
8
|
+
#
|
9
|
+
class Bitstring
|
10
|
+
include Erlang::Term
|
11
|
+
include Erlang::Immutable
|
12
|
+
|
13
|
+
# Return the data for this `Bitstring`
|
14
|
+
# @return [::String]
|
15
|
+
attr_reader :data
|
16
|
+
|
17
|
+
# Return the bits for this `Bitstring`
|
18
|
+
# @return [::Integer]
|
19
|
+
attr_reader :bits
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# Create a new `Bitstring` populated with the given `data` and `bits`.
|
23
|
+
# @param data [::String, Symbol, ::Enumerable, Integer] The content of the `Binary`
|
24
|
+
# @param bits [Integer] The number of bits to keep on the last byte
|
25
|
+
# @return [Bitstring, Binary]
|
26
|
+
# @raise [ArgumentError] if `data` cannot be coerced to be a `::String` or `bits` is not between 1 and 8
|
27
|
+
def [](*data, bits: 8)
|
28
|
+
return EmptyBinary if data.empty?
|
29
|
+
raise ArgumentError, 'bits must be an Integer' if not bits.is_a?(::Integer)
|
30
|
+
raise ArgumentError, 'bits must be between 1 and 8' if bits < 1 or bits > 8
|
31
|
+
binary = Erlang.iolist_to_binary(data)
|
32
|
+
if bits == 8 or binary.empty?
|
33
|
+
return binary
|
34
|
+
else
|
35
|
+
return new(binary, bits)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def empty
|
40
|
+
return Erlang::EmptyBinary
|
41
|
+
end
|
42
|
+
|
43
|
+
# Compares `a` and `b` and returns whether they are less than,
|
44
|
+
# equal to, or greater than each other.
|
45
|
+
#
|
46
|
+
# @param a [Bitstring, Binary] The left argument
|
47
|
+
# @param b [Bitstring, Binary] The right argument
|
48
|
+
# @return [-1, 0, 1]
|
49
|
+
# @raise [ArgumentError] if `a` or `b` is not a `Bitstring` or `Binary`
|
50
|
+
def compare(a, b)
|
51
|
+
raise ArgumentError, "'a' must be of Erlang::Binary or Erlang::Bitstring type" if not a.kind_of?(Erlang::Binary) and not a.kind_of?(Erlang::Bitstring)
|
52
|
+
raise ArgumentError, "'b' must be of Erlang::Binary or Erlang::Bitstring type" if not b.kind_of?(Erlang::Binary) and not b.kind_of?(Erlang::Bitstring)
|
53
|
+
c = a.bitsize <=> b.bitsize
|
54
|
+
return a.data <=> b.data if c == 0
|
55
|
+
c = 0
|
56
|
+
i = 0
|
57
|
+
abytes = a.bytesize
|
58
|
+
bbytes = b.bytesize
|
59
|
+
abytes -= 1 if a.bits != 8
|
60
|
+
bbytes -= 1 if b.bits != 8
|
61
|
+
while c == 0 and i < abytes and i < bbytes
|
62
|
+
c = a.data.getbyte(i) <=> b.data.getbyte(i)
|
63
|
+
i += 1
|
64
|
+
end
|
65
|
+
if c == 0
|
66
|
+
if (a.bits != 8 and i == abytes and i < bbytes) or (b.bits != 8 and i == bbytes and i < abytes) or (a.bits != 8 and b.bits != 8)
|
67
|
+
abyte = a.data.getbyte(i)
|
68
|
+
bbyte = b.data.getbyte(i)
|
69
|
+
askip = 8 - a.bits
|
70
|
+
bskip = 8 - b.bits
|
71
|
+
i = 0
|
72
|
+
loop do
|
73
|
+
if i == a.bits and i == b.bits
|
74
|
+
c = 0
|
75
|
+
break
|
76
|
+
elsif i == a.bits
|
77
|
+
c = -1
|
78
|
+
break
|
79
|
+
elsif i == b.bits
|
80
|
+
c = 1
|
81
|
+
break
|
82
|
+
end
|
83
|
+
abit = (abyte >> (7 - ((i + askip) & 7))) & 1
|
84
|
+
bbit = (bbyte >> (7 - ((i + bskip) & 7))) & 1
|
85
|
+
c = abit <=> bbit
|
86
|
+
break if c != 0
|
87
|
+
i += 1
|
88
|
+
break if (i & 7) == 0
|
89
|
+
end
|
90
|
+
elsif i >= a.bytesize and i < b.bytesize
|
91
|
+
c = -1
|
92
|
+
elsif i >= b.bytesize and i < a.bytesize
|
93
|
+
c = 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
return c
|
97
|
+
end
|
98
|
+
|
99
|
+
# Concatenates list of `Binary` or `Bitstring` items into a single `Binary` or `Bitstring`.
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# Erlang::Bitstring.concat(Erlang::Bitstring[1, bits: 2], Erlang::Binary[255])
|
103
|
+
# # => Erlang::Bitstring[127, 3, bits: 2]
|
104
|
+
#
|
105
|
+
# @param iodata [Binary, Bitstring] The list of bitstrings
|
106
|
+
# @return [Binary, Bitstring]
|
107
|
+
def concat(*iodata)
|
108
|
+
return iodata.reduce(Erlang::EmptyBinary) { |acc, item| acc.concat(item) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @private
|
113
|
+
def initialize(data = nil, bits = 8)
|
114
|
+
raise ArgumentError, 'bits must be an Integer' if not bits.is_a?(::Integer)
|
115
|
+
raise ArgumentError, 'bits must be between 1 and 8' if bits < 1 or bits > 8
|
116
|
+
data = ::String.new if data.nil?
|
117
|
+
data = data.data if data.is_a?(Erlang::Binary)
|
118
|
+
raise ArgumentError, 'data must be a String' if not data.is_a?(::String)
|
119
|
+
data = Erlang::Terms.binary_encoding(data)
|
120
|
+
if data.bytesize > 0
|
121
|
+
data.setbyte(-1, ((data.getbyte(-1) << (8 - bits)) & 255) >> (8 - bits))
|
122
|
+
end
|
123
|
+
@data = data.freeze
|
124
|
+
@bits = bits.freeze
|
125
|
+
@bitsize = (@data.bytesize == 0) ? 0 : (((@data.bytesize - 1) * 8) + @bits)
|
126
|
+
end
|
127
|
+
|
128
|
+
# @private
|
129
|
+
def hash
|
130
|
+
hash = @data.hash
|
131
|
+
return (hash << 5) - hash + bits.hash
|
132
|
+
end
|
133
|
+
|
134
|
+
# @raise [NotImplementedError]
|
135
|
+
def at(position)
|
136
|
+
raise NotImplementedError
|
137
|
+
end
|
138
|
+
alias :[] :at
|
139
|
+
|
140
|
+
# @return [Integer] the number of bits in this `Bitstring`
|
141
|
+
def bitsize
|
142
|
+
return @bitsize
|
143
|
+
end
|
144
|
+
|
145
|
+
# @private
|
146
|
+
BIT_PACK = 'B*'.freeze
|
147
|
+
|
148
|
+
# Return specific objects from the `Bitstring`. All overloads return `nil` if
|
149
|
+
# the starting index is out of range.
|
150
|
+
#
|
151
|
+
# @overload bitslice(index)
|
152
|
+
# Returns a single bit at the given `index`. If `index` is negative,
|
153
|
+
# count backwards from the end.
|
154
|
+
#
|
155
|
+
# @param index [Integer] The index to retrieve. May be negative.
|
156
|
+
# @return [Integer, nil]
|
157
|
+
# @example
|
158
|
+
# b = Erlang::Bitstring[2, bits: 2]
|
159
|
+
# b.bitslice(0) # => 1
|
160
|
+
# b.bitslice(1) # => 0
|
161
|
+
# b.bitslice(-1) # => 0
|
162
|
+
# b.bitslice(2) # => nil
|
163
|
+
#
|
164
|
+
# @overload bitslice(index, length)
|
165
|
+
# Return a bitstring starting at `index` and continuing for `length`
|
166
|
+
# bits or until the end of the `Bitstring`, whichever occurs first.
|
167
|
+
#
|
168
|
+
# @param start [Integer] The index to start retrieving bits from. May be
|
169
|
+
# negative.
|
170
|
+
# @param length [Integer] The number of bits to retrieve.
|
171
|
+
# @return [Bitstring, Binary]
|
172
|
+
# @example
|
173
|
+
# b = Erlang::Bitstring[1, 117, bits: 7]
|
174
|
+
# b.bitslice(0, 11) # => Erlang::Bitstring[1, 7, bits: 3]
|
175
|
+
# b.bitslice(11, 4) # => Erlang::Bitstring[5, bits: 4]
|
176
|
+
# b.bitslice(16, 1) # => nil
|
177
|
+
#
|
178
|
+
# @overload bitslice(index..end)
|
179
|
+
# Return a bitstring starting at `index` and continuing to index
|
180
|
+
# `end` or the end of the `Bitstring`, whichever occurs first.
|
181
|
+
#
|
182
|
+
# @param range [Range] The range of bits to retrieve.
|
183
|
+
# @return [Bitstring, Binary]
|
184
|
+
# @example
|
185
|
+
# b = Erlang::Bitstring[1, 117, bits: 7]
|
186
|
+
# b.bitslice(0...11) # => Erlang::Bitstring[1, 7, bits: 3]
|
187
|
+
# b.bitslice(11...15) # => Erlang::Bitstring[5, bits: 4]
|
188
|
+
# b.bitslice(16..-1) # => nil
|
189
|
+
#
|
190
|
+
# @see Erlang::Binary#bitslice
|
191
|
+
def bitslice(arg, length = (missing_length = true))
|
192
|
+
if missing_length
|
193
|
+
if arg.is_a?(Range)
|
194
|
+
from, to = arg.begin, arg.end
|
195
|
+
from += bitsize if from < 0
|
196
|
+
return nil if from < 0
|
197
|
+
to += bitsize if to < 0
|
198
|
+
to += 1 if !arg.exclude_end?
|
199
|
+
length = to - from
|
200
|
+
length = 0 if length < 0
|
201
|
+
length = bitsize - from if (from + length) > bitsize
|
202
|
+
return nil if length < 0
|
203
|
+
l8 = length.div(8)
|
204
|
+
l1 = length % 8
|
205
|
+
pad = 8 - l1
|
206
|
+
enum = each_bit
|
207
|
+
skip = from
|
208
|
+
enum = enum.drop_while {
|
209
|
+
if skip > 0
|
210
|
+
skip -= 1
|
211
|
+
next true
|
212
|
+
else
|
213
|
+
next false
|
214
|
+
end
|
215
|
+
}
|
216
|
+
head = enum.take(length)
|
217
|
+
if l1 == 0
|
218
|
+
return Erlang::Binary[[head.join].pack(BIT_PACK)]
|
219
|
+
else
|
220
|
+
tail = head[-l1..-1]
|
221
|
+
head = head[0...-l1]
|
222
|
+
tail = ([0] * pad).concat(tail)
|
223
|
+
return Erlang::Bitstring[[[head.join, tail.join].join].pack(BIT_PACK), bits: l1]
|
224
|
+
end
|
225
|
+
else
|
226
|
+
arg += bitsize if arg < 0
|
227
|
+
return nil if arg < 0
|
228
|
+
return nil if arg >= bitsize
|
229
|
+
a8 = arg.div(8)
|
230
|
+
a1 = arg % 8
|
231
|
+
byte = @data.getbyte(a8)
|
232
|
+
return nil if byte.nil?
|
233
|
+
return (byte >> ((@bits - a1 - 1) & 7)) & 1
|
234
|
+
end
|
235
|
+
else
|
236
|
+
return nil if length < 0
|
237
|
+
arg += bitsize if arg < 0
|
238
|
+
return nil if arg < 0
|
239
|
+
length = bitsize - arg if (arg + length) > bitsize
|
240
|
+
return nil if length < 0
|
241
|
+
l8 = length.div(8)
|
242
|
+
l1 = length % 8
|
243
|
+
pad = 8 - l1
|
244
|
+
enum = each_bit
|
245
|
+
skip = arg
|
246
|
+
enum = enum.drop_while {
|
247
|
+
if skip > 0
|
248
|
+
skip -= 1
|
249
|
+
next true
|
250
|
+
else
|
251
|
+
next false
|
252
|
+
end
|
253
|
+
}
|
254
|
+
head = enum.take(length)
|
255
|
+
if l1 == 0
|
256
|
+
return Erlang::Binary[[head.join].pack(BIT_PACK)]
|
257
|
+
else
|
258
|
+
tail = head[-l1..-1]
|
259
|
+
head = head[0...-l1]
|
260
|
+
tail = ([0] * pad).concat(tail)
|
261
|
+
return Erlang::Bitstring[[[head.join, tail.join].join].pack(BIT_PACK), bits: l1]
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# @return [Integer] the number of bytes in this `Bitstring`
|
267
|
+
def bytesize
|
268
|
+
return @data.bytesize
|
269
|
+
end
|
270
|
+
alias :size :bytesize
|
271
|
+
|
272
|
+
# Concatenates list of `Binary` or `Bitstring` items into a single `Binary` or `Bitstring`.
|
273
|
+
#
|
274
|
+
# @example
|
275
|
+
# Erlang::Bitstring[3, bits: 3].concat(Erlang::Bitstring[1, bits: 5])
|
276
|
+
# # => "a"
|
277
|
+
#
|
278
|
+
# @param iodata [Binary, Bitstring] The list of bitstrings
|
279
|
+
# @return [Binary, Bitstring]
|
280
|
+
def concat(*other)
|
281
|
+
if other.size == 1 and (other[0].kind_of?(Erlang::Binary) or other[0].kind_of?(Erlang::Bitstring))
|
282
|
+
other = other[0]
|
283
|
+
else
|
284
|
+
other = Erlang::Binary[*other]
|
285
|
+
end
|
286
|
+
return other if empty?
|
287
|
+
return self if other.empty?
|
288
|
+
if @bits == 8
|
289
|
+
return to_binary.concat(other)
|
290
|
+
else
|
291
|
+
bits = (@bits + other.bits) % 8
|
292
|
+
head = [*each_bit, *other.each_bit]
|
293
|
+
if bits == 0
|
294
|
+
return Erlang::Binary[[head.join].pack(BIT_PACK)]
|
295
|
+
else
|
296
|
+
pad = 8 - bits
|
297
|
+
tail = head[-bits..-1]
|
298
|
+
head = head[0...-bits]
|
299
|
+
tail = ([0] * pad).concat(tail)
|
300
|
+
return Erlang::Bitstring[[[head.join, tail.join].join].pack(BIT_PACK), bits: bits]
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
alias :+ :concat
|
305
|
+
|
306
|
+
# @raise [NotImplementedError]
|
307
|
+
def copy(n = 1)
|
308
|
+
raise NotImplementedError
|
309
|
+
end
|
310
|
+
|
311
|
+
# @raise [NotImplementedError]
|
312
|
+
def decode_unsigned(endianness = :big)
|
313
|
+
raise NotImplementedError
|
314
|
+
end
|
315
|
+
|
316
|
+
# Call the given block once for each bit in the `Bitstring`, passing each
|
317
|
+
# bit from first to last successively to the block. If no block is given,
|
318
|
+
# returns an `Enumerator`.
|
319
|
+
#
|
320
|
+
# @return [self]
|
321
|
+
# @yield [Integer]
|
322
|
+
def each_bit
|
323
|
+
return enum_for(:each_bit) unless block_given?
|
324
|
+
index = 0
|
325
|
+
headbits = (self.bytesize - 1) * 8
|
326
|
+
skipbits = 8 - @bits
|
327
|
+
@data.each_byte do |byte|
|
328
|
+
loop do
|
329
|
+
break if index == @bitsize
|
330
|
+
if index >= headbits
|
331
|
+
bit = (byte >> (7 - ((index + skipbits) & 7))) & 1
|
332
|
+
else
|
333
|
+
bit = (byte >> (7 - (index & 7))) & 1
|
334
|
+
end
|
335
|
+
yield bit
|
336
|
+
index += 1
|
337
|
+
break if (index & 7) == 0
|
338
|
+
end
|
339
|
+
end
|
340
|
+
return self
|
341
|
+
end
|
342
|
+
|
343
|
+
# Split the bits in this `Bitstring` in groups of `number` bits, and yield
|
344
|
+
# each group to the block (as a `List`). If no block is given, returns
|
345
|
+
# an `Enumerator`.
|
346
|
+
#
|
347
|
+
# @example
|
348
|
+
# b = Erlang::Bitstring[117, bits: 7]
|
349
|
+
# b.each_bitslice(4).to_a # => [Erlang::Bitstring[14, bits: 4], Erlang::Bitstring[5, bits: 3]]
|
350
|
+
#
|
351
|
+
# @return [self, Enumerator]
|
352
|
+
# @yield [Binary, Bitstring] Once for each bitstring.
|
353
|
+
# @see Erlang::Bitstring#each_bitslice
|
354
|
+
def each_bitslice(number)
|
355
|
+
return enum_for(:each_bitslice, number) unless block_given?
|
356
|
+
raise ArgumentError, 'number must be a positive Integer' if not number.is_a?(::Integer) or number < 1
|
357
|
+
slices = bitsize.div(number) + ((bitsize % number == 0) ? 0 : 1)
|
358
|
+
index = 0
|
359
|
+
loop do
|
360
|
+
break if slices == 0
|
361
|
+
yield(bitslice(index * number, number))
|
362
|
+
slices -= 1
|
363
|
+
index += 1
|
364
|
+
end
|
365
|
+
return self
|
366
|
+
end
|
367
|
+
|
368
|
+
# @raise [NotImplementedError]
|
369
|
+
def each_byte
|
370
|
+
raise NotImplementedError
|
371
|
+
end
|
372
|
+
|
373
|
+
# Returns true if this `Bitstring` is empty.
|
374
|
+
#
|
375
|
+
# @return [Boolean]
|
376
|
+
def empty?
|
377
|
+
return @data.empty?
|
378
|
+
end
|
379
|
+
|
380
|
+
# Return true if `other` has the same type and contents as this `Bitstring`.
|
381
|
+
#
|
382
|
+
# @param other [Object] The object to compare with
|
383
|
+
# @return [Boolean]
|
384
|
+
def eql?(other)
|
385
|
+
return true if other.equal?(self)
|
386
|
+
if instance_of?(other.class)
|
387
|
+
return !!(self.class.compare(self, other) == 0)
|
388
|
+
else
|
389
|
+
return !!(Erlang.compare(other, self) == 0)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
alias :== :eql?
|
393
|
+
|
394
|
+
# @raise [NotImplementedError]
|
395
|
+
def first
|
396
|
+
raise NotImplementedError
|
397
|
+
end
|
398
|
+
|
399
|
+
# @raise [NotImplementedError]
|
400
|
+
def last
|
401
|
+
raise NotImplementedError
|
402
|
+
end
|
403
|
+
|
404
|
+
# @raise [NotImplementedError]
|
405
|
+
def part(position, length)
|
406
|
+
raise NotImplementedError
|
407
|
+
end
|
408
|
+
|
409
|
+
# Return the contents of this `Bitstring` as a Erlang-readable `::String`.
|
410
|
+
#
|
411
|
+
# @example
|
412
|
+
# Erlang::Bitstring["test", bits: 3].erlang_inspect
|
413
|
+
# # => "<<116,101,115,4:3>>"
|
414
|
+
#
|
415
|
+
# @return [::String]
|
416
|
+
def erlang_inspect(raw = false)
|
417
|
+
return Erlang.inspect(Erlang::Binary.new(@data), raw: raw) if @bits == 8
|
418
|
+
result = '<<'
|
419
|
+
bytes = @data.bytes
|
420
|
+
if last = bytes.pop
|
421
|
+
result << bytes.join(',')
|
422
|
+
result << ',' if not bytes.empty?
|
423
|
+
if @bits == 8
|
424
|
+
result << "#{last}"
|
425
|
+
else
|
426
|
+
result << "#{last}:#{@bits}"
|
427
|
+
end
|
428
|
+
end
|
429
|
+
result << '>>'
|
430
|
+
return result
|
431
|
+
end
|
432
|
+
|
433
|
+
# @return [::String] the nicely formatted version of the `Bitstring`
|
434
|
+
def inspect
|
435
|
+
return "Erlang::Bitstring[#{data.bytes.inspect[1..-2]}, bits: #{bits.inspect}]"
|
436
|
+
end
|
437
|
+
|
438
|
+
# @return [Binary] the `Binary` version of the `Bitstring` padded with zeroes
|
439
|
+
def to_binary
|
440
|
+
return EmptyBinary if empty?
|
441
|
+
return Erlang::Binary[@data]
|
442
|
+
end
|
443
|
+
|
444
|
+
# @return [::String] the string version of the `Bitstring`
|
445
|
+
def to_s
|
446
|
+
return @data
|
447
|
+
end
|
448
|
+
alias :to_str :to_s
|
449
|
+
|
450
|
+
# @return [::String]
|
451
|
+
# @private
|
452
|
+
def marshal_dump
|
453
|
+
return [@data, @bits]
|
454
|
+
end
|
455
|
+
|
456
|
+
# @private
|
457
|
+
def marshal_load(args)
|
458
|
+
data, bits = args
|
459
|
+
initialize(data, bits)
|
460
|
+
__send__(:immutable!)
|
461
|
+
return self
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|