Class: Marshal::ReadBuffer
- Defined in:
- opal/opal/corelib/marshal/read_buffer.rb
Instance Attribute Summary collapse
-
#buffer ⇒ Object
readonly
Returns the value of attribute buffer.
-
#index ⇒ Object
readonly
Returns the value of attribute index.
-
#object_cache ⇒ Object
readonly
Returns the value of attribute object_cache.
-
#symbols_cache ⇒ Object
readonly
Returns the value of attribute symbols_cache.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Instance Method Summary collapse
-
#initialize(input) ⇒ ReadBuffer
constructor
A new instance of ReadBuffer.
- #length ⇒ Object
- #read(cache: true) ⇒ Object
-
#read_array ⇒ Object
Reads and returns an array from an input stream.
-
#read_bignum ⇒ Object
Reads and returns Bignum from an input stream.
- #read_byte ⇒ Object
-
#read_cached_object ⇒ Object
Reads an object that was cached previously by its link.
-
#read_cached_symbol ⇒ Object
Reads a symbol that was previously cache by its link.
- #read_char ⇒ Object
-
#read_class ⇒ Object
Reads and returns a Class from an input stream.
-
#read_extended_object ⇒ Object
Reads an object that was dynamically extended before marshaling like.
-
#read_fixnum ⇒ Object
Reads and returns a fixnum from an input stream.
-
#read_float ⇒ Object
Reads and returns Float from an input stream.
-
#read_hash(cache: true) ⇒ Object
Reads and returns a hash from an input stream Sometimes hash shouldn't be cached using an internal object cache, for a: + hash of instance variables + hash of struct attributes.
-
#read_hashdef ⇒ Object
Reads and returns a hash with default value.
-
#read_module ⇒ Object
Reads and returns a Module from an input stream.
-
#read_object ⇒ Object
Reads and returns an abstract object from an input stream.
-
#read_primitive_with_ivars ⇒ Object
Reads a primitive object with instance variables (classes that have their own marshalling rules, like Array/Hash/Regexp/etc).
-
#read_regexp ⇒ Object
Reads and returns Regexp from an input stream.
-
#read_string(cache: true) ⇒ Object
Reads and returns a string from an input stream Sometimes string shouldn't be cached using an internal object cache, for a: + class/module name + string representation of float + string representation of regexp.
-
#read_struct ⇒ Object
Reads and returns a Struct from an input stream.
-
#read_symbol ⇒ Object
Reads and returns a symbol from an input stream.
-
#read_user_class ⇒ Object
Reads and User Class (instance of String/Regexp/Array/Hash subclass).
-
#read_user_defined ⇒ Object
Reads a 'User Defined' object that has '_dump/self._load' methods.
-
#read_user_marshal ⇒ Object
Reads a 'User Marshal' object that has 'marshal_dump/marshal_load' methods.
-
#safe_const_get(const_name) ⇒ Object
Returns a constant by passed const_name, re-raises Marshal-specific error when it's missing.
Constructor Details
#initialize(input) ⇒ ReadBuffer
Returns a new instance of ReadBuffer
23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 23 def initialize(input) @buffer = `stringToBytes(#{input.to_s})` @index = 0 major = read_byte minor = read_byte if major != MAJOR_VERSION || minor != MINOR_VERSION raise TypeError, "incompatible marshal file format (can't be read)" end @version = "#{major}.#{minor}" @object_cache = [] @symbols_cache = [] @ivars = [] end |
Instance Attribute Details
#buffer ⇒ Object (readonly)
Returns the value of attribute buffer
21 22 23 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 21 def buffer @buffer end |
#index ⇒ Object (readonly)
Returns the value of attribute index
21 22 23 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 21 def index @index end |
#object_cache ⇒ Object (readonly)
Returns the value of attribute object_cache
21 22 23 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 21 def object_cache @object_cache end |
#symbols_cache ⇒ Object (readonly)
Returns the value of attribute symbols_cache
21 22 23 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 21 def symbols_cache @symbols_cache end |
#version ⇒ Object (readonly)
Returns the value of attribute version
21 22 23 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 21 def version @version end |
Instance Method Details
#length ⇒ Object
37 38 39 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 37 def length @buffer.length end |
#read(cache: true) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 41 def read(cache: true) code = read_char # The first character indicates the type of the object case code when '0' nil when 'T' true when 'F' false when 'i' read_fixnum when 'f' read_float when 'l' read_bignum when '"' read_string when ':' read_symbol when ';' read_cached_symbol when '[' read_array when '{' read_hash when '}' read_hashdef when '/' read_regexp when 'S' read_struct when 'c' read_class when 'm' read_module when 'o' read_object when '@' read_cached_object when 'e' read_extended_object when 'I' read_primitive_with_ivars when 'C' read_user_class when 'u' read_user_defined when 'U' read_user_marshal when 'M' raise NotImplementedError, 'ModuleOld type cannot be demarshaled yet' # read_module_old when 'd' raise NotImplementedError, 'Data type cannot be demarshaled' else raise ArgumentError, 'dump format error' end end |
#read_array ⇒ Object
Reads and returns an array from an input stream
is encoded as '[', 3, 100, 200, 300
243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 243 def read_array result = [] @object_cache << result length = read_fixnum %x{ if (length > 0) { while (result.length < length) { result.push(#{read}); } } return result; } end |
#read_bignum ⇒ Object
Reads and returns Bignum from an input stream
172 173 174 175 176 177 178 179 180 181 182 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 172 def read_bignum sign = read_char == '-' ? -1 : 1 size = read_fixnum * 2 result = 0 (0...size).each do |exp| result += read_char.ord * 2**(exp * 8) end result = result.to_i * sign @object_cache << result result end |
#read_byte ⇒ Object
100 101 102 103 104 105 106 107 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 100 def read_byte if @index >= length raise ArgumentError, 'marshal data too short' end result = @buffer[@index] @index += 1 result end |
#read_cached_object ⇒ Object
Reads an object that was cached previously by its link
is encoded as [obj1, @1, obj2, @2, obj3, @3]
NOTE: array itself is cached as @0, that's why obj1 is cached a @1, obj2 is @2, etc.
421 422 423 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 421 def read_cached_object object_cache[read_fixnum] end |
#read_cached_symbol ⇒ Object
Reads a symbol that was previously cache by its link
Is encoded as '[', 6, :a, @0, :b, @1, :c, @2
232 233 234 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 232 def read_cached_symbol symbols_cache[read_fixnum] end |
#read_char ⇒ Object
109 110 111 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 109 def read_char `String.fromCharCode(#{read_byte})` end |
#read_class ⇒ Object
Reads and returns a Class from an input stream
is encoded as 'c', 'String'
345 346 347 348 349 350 351 352 353 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 345 def read_class klass_name = read_string(cache: false) result = safe_const_get(klass_name) unless result.class == Class raise ArgumentError, "#{klass_name} does not refer to a Class" end @object_cache << result result end |
#read_extended_object ⇒ Object
Reads an object that was dynamically extended before marshaling like
is encoded as 'e', :M2, :M1, obj
437 438 439 440 441 442 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 437 def read_extended_object mod = safe_const_get(read) object = read object.extend(mod) object end |
#read_fixnum ⇒ Object
Reads and returns a fixnum from an input stream
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 115 def read_fixnum %x{ var x, i, c = (#{read_byte} ^ 128) - 128; if (c === 0) { return 0; } if (c > 0) { if (4 < c && c < 128) { return c - 5; } x = 0; for (i = 0; i < c; i++) { x |= (#{read_byte} << (8*i)); } } else { if (-129 < c && c < -4) { return c + 5; } c = -c; x = -1; for (i = 0; i < c; i++) { x &= ~(0xff << (8*i)); x |= (#{read_byte} << (8*i)); } } return x; } end |
#read_float ⇒ Object
Reads and returns Float from an input stream
Is encoded as 'f', '123.456'
155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 155 def read_float s = read_string(cache: false) result = if s == 'nan' 0.0 / 0 elsif s == 'inf' 1.0 / 0 elsif s == '-inf' -1.0 / 0 else s.to_f end @object_cache << result result end |
#read_hash(cache: true) ⇒ Object
Reads and returns a hash from an input stream Sometimes hash shouldn't be cached using an internal object cache, for a:
- hash of instance variables
- hash of struct attributes
is encoded as '{', 2, 100, 200, 300, 400
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 269 def read_hash(cache: true) result = {} if cache @object_cache << result end length = read_fixnum %x{ if (length > 0) { var key, value, i; for (i = 0; i < #{length}; i++) { key = #{read}; value = #{read}; #{result[`key`] = `value`}; } } return result; } end |
#read_hashdef ⇒ Object
Reads and returns a hash with default value
is encoded as '}', 1, 100, 200, :default
297 298 299 300 301 302 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 297 def read_hashdef hash = read_hash default_value = read hash.default = default_value hash end |
#read_module ⇒ Object
Reads and returns a Module from an input stream
is encoded as 'm', 'Kernel'
362 363 364 365 366 367 368 369 370 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 362 def read_module mod_name = read_string(cache: false) result = safe_const_get(mod_name) unless result.class == Module raise ArgumentError, "#{mod_name} does not refer to a Module" end @object_cache << result result end |
#read_object ⇒ Object
Reads and returns an abstract object from an input stream
is encoded as 'o', :Object, => 100
The only exception is a Range class (and its subclasses) For some reason in MRI isntances of this class have instance variables
- begin
- end
- excl without '@' perfix.
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 388 def read_object klass_name = read(cache: false) klass = safe_const_get(klass_name) object = klass.allocate @object_cache << object ivars = read_hash(cache: false) ivars.each do |name, value| if name[0] == '@' object.instance_variable_set(name, value) else # MRI allows an object to have ivars that do not start from '@' # https://github.com/ruby/ruby/blob/ab3a40c1031ff3a0535f6bcf26de40de37dbb1db/range.c#L1225 `object[name] = value` end end object end |
#read_primitive_with_ivars ⇒ Object
Reads a primitive object with instance variables (classes that have their own marshalling rules, like Array/Hash/Regexp/etc)
is encoded as 'I', [100, 200, 300], => value
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 454 def read_primitive_with_ivars object = read primitive_ivars = read_hash(cache: false) if primitive_ivars.any? && object.is_a?(String) object = `new String(object)` end primitive_ivars.each do |name, value| if name != 'E' object.instance_variable_set(name, value) end end object end |
#read_regexp ⇒ Object
Reads and returns Regexp from an input stream
is encoded as '/', 'regexp', r.options.chr
311 312 313 314 315 316 317 318 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 311 def read_regexp string = read_string(cache: false) = read_byte result = Regexp.new(string, ) @object_cache << result result end |
#read_string(cache: true) ⇒ Object
Reads and returns a string from an input stream Sometimes string shouldn't be cached using an internal object cache, for a:
- class/module name
- string representation of float
- string representation of regexp
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 191 def read_string(cache: true) length = read_fixnum %x{ var i, result = ''; for (i = 0; i < length; i++) { result += #{read_char}; } if (cache) { self.object_cache.push(result); } return result; } end |
#read_struct ⇒ Object
Reads and returns a Struct from an input stream
is encoded as 'S', :Point, => 100, :y => 200
328 329 330 331 332 333 334 335 336 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 328 def read_struct klass_name = read(cache: false) klass = safe_const_get(klass_name) attributes = read_hash(cache: false) args = attributes.values_at(*klass.members) result = klass.new(*args) @object_cache << result result end |
#read_symbol ⇒ Object
Reads and returns a symbol from an input stream
210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 210 def read_symbol length = read_fixnum %x{ var i, result = ''; for (i = 0; i < length; i++) { result += #{read_char}; } self.symbols_cache.push(result); return result; } end |
#read_user_class ⇒ Object
Reads and User Class (instance of String/Regexp/Array/Hash subclass)
is encoded as 'C', :UserArray, [100, 200, 300]
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 480 def read_user_class klass_name = read(cache: false) klass = safe_const_get(klass_name) value = read(cache: false) result = if klass < Hash klass[value] else klass.new(value) end @object_cache << result result end |
#read_user_defined ⇒ Object
Reads a 'User Defined' object that has '_dump/self._load' methods
is encoded as 'u', :UserDefined, '_dumped'
To load it back UserDefined._load' must be used.
511 512 513 514 515 516 517 518 519 520 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 511 def read_user_defined klass_name = read(cache: false) klass = safe_const_get(klass_name) data = read_string(cache: false) result = klass._load(data) @object_cache << result result end |
#read_user_marshal ⇒ Object
Reads a 'User Marshal' object that has 'marshal_dump/marshal_load' methods
is encoded as 'U', :UserMarshal, [100, 200]
To load it back UserMarshal.allocate
and UserMarshal#marshal_load
must be called
541 542 543 544 545 546 547 548 549 550 551 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 541 def read_user_marshal klass_name = read(cache: false) klass = safe_const_get(klass_name) result = klass.allocate @object_cache << result data = read(cache: false) result.marshal_load(data) result end |
#safe_const_get(const_name) ⇒ Object
Returns a constant by passed const_name, re-raises Marshal-specific error when it's missing
556 557 558 559 560 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 556 def safe_const_get(const_name) Object.const_get(const_name) rescue NameError raise ArgumentError, "undefined class/module #{const_name}" end |