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 99 |
# 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 result = 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 result end |
#read_array ⇒ Object
Reads and returns an array from an input stream
is encoded as '[', 3, 100, 200, 300
244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 244 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
173 174 175 176 177 178 179 180 181 182 183 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 173 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
101 102 103 104 105 106 107 108 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 101 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.
422 423 424 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 422 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
233 234 235 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 233 def read_cached_symbol symbols_cache[read_fixnum] end |
#read_char ⇒ Object
110 111 112 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 110 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'
346 347 348 349 350 351 352 353 354 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 346 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
438 439 440 441 442 443 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 438 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
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 147 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 116 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'
156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 156 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
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 270 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
298 299 300 301 302 303 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 298 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'
363 364 365 366 367 368 369 370 371 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 363 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.
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 389 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
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 455 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
312 313 314 315 316 317 318 319 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 312 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
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 192 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
329 330 331 332 333 334 335 336 337 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 329 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
211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 211 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]
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 481 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.
512 513 514 515 516 517 518 519 520 521 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 512 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
542 543 544 545 546 547 548 549 550 551 552 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 542 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
557 558 559 560 561 562 563 |
# File 'opal/opal/corelib/marshal/read_buffer.rb', line 557 def safe_const_get(const_name) begin Object.const_get(const_name) rescue NameError raise ArgumentError, "undefined class/module #{const_name}" end end |