msgpack 1.3.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +57 -0
  3. data/.rubocop.yml +2 -2
  4. data/ChangeLog +74 -0
  5. data/Gemfile +1 -1
  6. data/README.md +266 -0
  7. data/Rakefile +1 -9
  8. data/bench/bench.rb +78 -0
  9. data/bin/console +8 -0
  10. data/doclib/msgpack/factory.rb +47 -3
  11. data/doclib/msgpack/packer.rb +5 -4
  12. data/doclib/msgpack/unpacker.rb +2 -2
  13. data/ext/java/org/msgpack/jruby/Buffer.java +23 -16
  14. data/ext/java/org/msgpack/jruby/Decoder.java +46 -23
  15. data/ext/java/org/msgpack/jruby/Encoder.java +68 -30
  16. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +37 -49
  17. data/ext/java/org/msgpack/jruby/ExtensionValue.java +5 -8
  18. data/ext/java/org/msgpack/jruby/Factory.java +47 -7
  19. data/ext/java/org/msgpack/jruby/Packer.java +29 -17
  20. data/ext/java/org/msgpack/jruby/Unpacker.java +72 -37
  21. data/ext/msgpack/buffer.c +42 -68
  22. data/ext/msgpack/buffer.h +59 -14
  23. data/ext/msgpack/buffer_class.c +90 -52
  24. data/ext/msgpack/compat.h +1 -111
  25. data/ext/msgpack/extconf.rb +45 -19
  26. data/ext/msgpack/factory_class.c +133 -43
  27. data/ext/msgpack/packer.c +60 -36
  28. data/ext/msgpack/packer.h +27 -25
  29. data/ext/msgpack/packer_class.c +84 -77
  30. data/ext/msgpack/packer_class.h +11 -0
  31. data/ext/msgpack/packer_ext_registry.c +24 -32
  32. data/ext/msgpack/packer_ext_registry.h +40 -33
  33. data/ext/msgpack/sysdep.h +5 -2
  34. data/ext/msgpack/unpacker.c +132 -115
  35. data/ext/msgpack/unpacker.h +23 -10
  36. data/ext/msgpack/unpacker_class.c +83 -78
  37. data/ext/msgpack/unpacker_class.h +11 -0
  38. data/ext/msgpack/unpacker_ext_registry.c +42 -18
  39. data/ext/msgpack/unpacker_ext_registry.h +23 -16
  40. data/lib/msgpack/bigint.rb +69 -0
  41. data/lib/msgpack/factory.rb +103 -0
  42. data/lib/msgpack/symbol.rb +21 -4
  43. data/lib/msgpack/time.rb +1 -1
  44. data/lib/msgpack/version.rb +4 -8
  45. data/lib/msgpack.rb +6 -12
  46. data/msgpack.gemspec +4 -6
  47. data/spec/bigint_spec.rb +26 -0
  48. data/spec/cruby/buffer_spec.rb +17 -0
  49. data/spec/factory_spec.rb +351 -12
  50. data/spec/msgpack_spec.rb +1 -1
  51. data/spec/packer_spec.rb +18 -0
  52. data/spec/spec_helper.rb +37 -3
  53. data/spec/timestamp_spec.rb +38 -0
  54. data/spec/unpacker_spec.rb +157 -4
  55. metadata +31 -61
  56. data/.travis.yml +0 -43
  57. data/README.rdoc +0 -225
  58. data/bench/pack.rb +0 -23
  59. data/bench/pack_log.rb +0 -33
  60. data/bench/pack_log_long.rb +0 -65
  61. data/bench/pack_symbols.rb +0 -28
  62. data/bench/run.sh +0 -14
  63. data/bench/run_long.sh +0 -35
  64. data/bench/run_symbols.sh +0 -26
  65. data/bench/unpack.rb +0 -21
  66. data/bench/unpack_log.rb +0 -34
  67. data/bench/unpack_log_long.rb +0 -67
data/spec/factory_spec.rb CHANGED
@@ -1,4 +1,3 @@
1
- # encoding: ascii-8bit
2
1
  require 'spec_helper'
3
2
 
4
3
  describe MessagePack::Factory do
@@ -42,6 +41,21 @@ describe MessagePack::Factory do
42
41
  unpacker.feed(MessagePack::ExtensionValue.new(1, 'a').to_msgpack)
43
42
  expect{ unpacker.read }.to raise_error(MessagePack::UnknownExtTypeError)
44
43
  end
44
+
45
+ it 'does not share the extension registry with unpackers' do
46
+ subject.register_type(0x00, Symbol)
47
+ expect do
48
+ unpacker = subject.unpacker
49
+ expect do
50
+ unpacker.register_type(0x01) {}
51
+ end.to change { unpacker.registered_types }
52
+
53
+ second_unpacker = subject.unpacker
54
+ expect do
55
+ second_unpacker.register_type(0x01) {}
56
+ end.to_not change { unpacker.registered_types }
57
+ end.to_not change { subject.registered_types }
58
+ end
45
59
  end
46
60
 
47
61
  describe '#dump and #load' do
@@ -255,34 +269,231 @@ describe MessagePack::Factory do
255
269
  subject { factory.packer.pack(value).to_s }
256
270
  before { stub_const('Value', Class.new{ include Mod }) }
257
271
  let(:value) { Value.new }
258
- it { is_expected.to eq "\xC7\x0F\x01value_msgpacked" }
272
+ it { is_expected.to eq "\xC7\x0F\x01value_msgpacked".force_encoding(Encoding::BINARY) }
259
273
  end
260
274
 
261
275
  describe "packing an object which has been extended by the module" do
262
276
  subject { factory.packer.pack(object).to_s }
263
277
  let(:object) { Object.new.extend Mod }
264
- it { is_expected.to eq "\xC7\x0F\x01value_msgpacked" }
278
+ it { is_expected.to eq "\xC7\x0F\x01value_msgpacked".force_encoding(Encoding::BINARY) }
265
279
  end
266
280
 
267
281
  describe "unpacking with the module" do
268
- subject { factory.unpacker.feed("\xC7\x06\x01module").unpack }
282
+ subject { factory.unpacker.feed("\xC7\x06\x01module".force_encoding(Encoding::BINARY)).unpack }
269
283
  it { is_expected.to eq "unpacked module" }
270
284
  end
271
285
  end
286
+
287
+ describe "registering an ext type for Integer" do
288
+ let(:factory) { described_class.new }
289
+ let(:bigint) { 10**150 }
290
+
291
+ it 'does not work by default without passing `oversized_integer_extension: true`' do
292
+ factory.register_type(0x01, Integer, packer: :to_s, unpacker: method(:Integer))
293
+
294
+ expect do
295
+ factory.dump(bigint)
296
+ end.to raise_error RangeError
297
+ end
298
+
299
+ it 'raises ArgumentError if the type is not Integer' do
300
+ expect do
301
+ factory.register_type(0x01, MyType, packer: :to_s, unpacker: method(:Integer), oversized_integer_extension: true)
302
+ end.to raise_error(ArgumentError)
303
+ end
304
+
305
+ it 'invokes the packer if registered with `oversized_integer_extension: true`' do
306
+ factory.register_type(0x01, Integer, packer: :to_s, unpacker: method(:Integer), oversized_integer_extension: true)
307
+
308
+ expect(factory.load(factory.dump(bigint))).to be == bigint
309
+ end
310
+
311
+ it 'does not use the oversized_integer_extension packer for integers fitting in native types' do
312
+ factory.register_type(
313
+ 0x01,
314
+ Integer,
315
+ packer: ->(int) { raise NotImplementedError },
316
+ unpacker: ->(payload) { raise NotImplementedError },
317
+ oversized_integer_extension: true
318
+ )
319
+
320
+ expect(factory.dump(42)).to eq(MessagePack.dump(42))
321
+ end
322
+ end
323
+
324
+ describe "registering ext type with recursive serialization" do
325
+ before do
326
+ stub_const("Point", Struct.new(:x, :y, :z))
327
+ end
328
+
329
+ it 'can receive the packer as argument (proc)' do
330
+ factory = MessagePack::Factory.new
331
+ factory.register_type(0x00, Symbol)
332
+ factory.register_type(
333
+ 0x01,
334
+ Point,
335
+ packer: ->(point, packer) do
336
+ packer.write(point.to_h)
337
+ nil
338
+ end,
339
+ unpacker: ->(unpacker) do
340
+ attrs = unpacker.read
341
+ Point.new(attrs.fetch(:x), attrs.fetch(:y), attrs.fetch(:z))
342
+ end,
343
+ recursive: true,
344
+ )
345
+
346
+ point = Point.new(1, 2, 3)
347
+ payload = factory.dump(point)
348
+ expect(factory.load(payload)).to be == point
349
+ end
350
+
351
+ it 'can receive the packer as argument (Method)' do
352
+ mod = Module.new
353
+ mod.define_singleton_method(:packer) do |point, packer|
354
+ packer.write(point.to_h)
355
+ nil
356
+ end
357
+
358
+ mod.define_singleton_method(:unpacker) do |unpacker|
359
+ attrs = unpacker.read
360
+ Point.new(attrs.fetch(:x), attrs.fetch(:y), attrs.fetch(:z))
361
+ end
362
+
363
+ factory = MessagePack::Factory.new
364
+ factory.register_type(0x00, Symbol)
365
+ factory.register_type(
366
+ 0x01,
367
+ Point,
368
+ packer: mod.method(:packer),
369
+ unpacker: mod.method(:unpacker),
370
+ recursive: true,
371
+ )
372
+
373
+ point = Point.new(1, 2, 3)
374
+ payload = factory.dump(point)
375
+ expect(factory.load(payload)).to be == point
376
+ end
377
+
378
+ it 'respect message pack format' do
379
+ factory = MessagePack::Factory.new
380
+ factory.register_type(0x00, Symbol)
381
+ factory.register_type(
382
+ 0x01,
383
+ Point,
384
+ packer: ->(point, packer) do
385
+ packer.write(point.to_a)
386
+ nil
387
+ end,
388
+ unpacker: ->(unpacker) do
389
+ attrs = unpacker.read
390
+ Point.new(*attrs)
391
+ end,
392
+ recursive: true,
393
+ )
394
+
395
+ point = Point.new(1, 2, 3)
396
+ expect(factory.dump(point)).to be == "\xD6\x01".b + MessagePack.dump([1, 2, 3])
397
+ end
398
+
399
+ it 'sets the correct length' do
400
+ factory = MessagePack::Factory.new
401
+ factory.register_type(0x00, Symbol)
402
+ factory.register_type(
403
+ 0x01,
404
+ Point,
405
+ packer: ->(point, packer) do
406
+ packer.write(point.to_h)
407
+ nil
408
+ end,
409
+ unpacker: ->(unpacker) do
410
+ attrs = unpacker.read
411
+ Point.new(attrs.fetch(:x), attrs.fetch(:y), attrs.fetch(:z))
412
+ end,
413
+ recursive: true,
414
+ )
415
+
416
+ point = Point.new(1, 2, 3)
417
+ payload = factory.dump([1, point, 3])
418
+
419
+ obj = MessagePack::Factory.new.load(payload, allow_unknown_ext: true)
420
+ expect(obj).to be == [
421
+ 1,
422
+ MessagePack::ExtensionValue.new(1, factory.dump(x: 1, y: 2, z: 3)),
423
+ 3,
424
+ ]
425
+
426
+ expect(factory.load(payload)).to be == [
427
+ 1,
428
+ Point.new(1, 2, 3),
429
+ 3,
430
+ ]
431
+ end
432
+
433
+ it 'can be nested' do
434
+ factory = MessagePack::Factory.new
435
+ factory.register_type(
436
+ 0x02,
437
+ Set,
438
+ packer: ->(set, packer) do
439
+ packer.write(set.to_a)
440
+ nil
441
+ end,
442
+ unpacker: ->(unpacker) do
443
+ unpacker.read.to_set
444
+ end,
445
+ recursive: true,
446
+ )
447
+
448
+ expected = Set[1, Set[2, Set[3]]]
449
+ payload = factory.dump(expected)
450
+ expect(payload).to be == "\xC7\v\x02\x92\x01\xC7\x06\x02\x92\x02\xD5\x02\x91\x03".b
451
+ expect(factory.load(factory.dump(expected))).to be == expected
452
+ end
453
+ end
272
454
  end
273
455
 
274
456
  describe 'the special treatment of symbols with ext type' do
275
- let(:packer) { subject.packer }
276
- let(:unpacker) { subject.unpacker }
457
+ def roundtrip(object, options = nil)
458
+ subject.load(subject.dump(object), options)
459
+ end
277
460
 
278
- def symbol_after_roundtrip
279
- packed_symbol = packer.pack(:symbol).to_s
280
- unpacker.feed(packed_symbol).unpack
461
+ context 'using the optimized symbol unpacker' do
462
+ before do
463
+ skip if IS_JRUBY # JRuby implementation doesn't support the optimized symbols unpacker for now
464
+ subject.register_type(
465
+ 0x00,
466
+ ::Symbol,
467
+ packer: :to_msgpack_ext,
468
+ unpacker: :from_msgpack_ext,
469
+ optimized_symbols_parsing: true,
470
+ )
471
+ end
472
+
473
+ it 'lets symbols survive a roundtrip' do
474
+ expect(roundtrip(:symbol)).to be :symbol
475
+ end
476
+
477
+ it 'works with empty symbol' do
478
+ expect(roundtrip(:"")).to be :""
479
+ end
480
+
481
+ it 'preserves encoding for ASCII symbols' do
482
+ expect(:symbol.encoding).to be Encoding::US_ASCII
483
+ expect(roundtrip(:symbol)).to be :symbol
484
+ expect(roundtrip(:symbol).encoding).to be Encoding::US_ASCII
485
+ end
486
+
487
+ it 'preserves encoding for UTF-8 symbols' do
488
+ expect(:"fée".encoding).to be Encoding::UTF_8
489
+ expect(roundtrip(:"fée").encoding).to be Encoding::UTF_8
490
+ expect(roundtrip(:"fée")).to be :"fée"
491
+ end
281
492
  end
282
493
 
283
494
  context 'if no ext type is registered for symbols' do
284
495
  it 'converts symbols to string' do
285
- expect(symbol_after_roundtrip).to eq 'symbol'
496
+ expect(roundtrip(:symbol)).to eq 'symbol'
286
497
  end
287
498
  end
288
499
 
@@ -291,7 +502,41 @@ describe MessagePack::Factory do
291
502
  before { subject.register_type(0x00, ::Symbol) }
292
503
 
293
504
  it 'lets symbols survive a roundtrip' do
294
- expect(symbol_after_roundtrip).to be :symbol
505
+ expect(roundtrip(:symbol)).to be :symbol
506
+ end
507
+
508
+ it 'works with hash keys' do
509
+ expect(roundtrip(symbol: 1)).to be == { symbol: 1 }
510
+ end
511
+
512
+ it 'works with frozen: true option' do
513
+ expect(roundtrip(:symbol, freeze: true)).to be :symbol
514
+ end
515
+
516
+ it 'preserves encoding for ASCII symbols' do
517
+ expect(:symbol.encoding).to be Encoding::US_ASCII
518
+ expect(roundtrip(:symbol)).to be :symbol
519
+ expect(roundtrip(:symbol).encoding).to be Encoding::US_ASCII
520
+ end
521
+
522
+ it 'preserves encoding for UTF-8 symbols' do
523
+ expect(:"fée".encoding).to be Encoding::UTF_8
524
+ expect(roundtrip(:"fée")).to be :"fée"
525
+ expect(roundtrip(:"fée").encoding).to be Encoding::UTF_8
526
+ end
527
+
528
+ it 'does not handle symbols in other encodings' do
529
+ symbol = "fàe".encode(Encoding::ISO_8859_1).to_sym
530
+ expect(symbol.encoding).to be Encoding::ISO_8859_1
531
+
532
+ if IS_JRUBY
533
+ # JRuby doesn't quite behave like MRI here.
534
+ # "fàe".force_encoding(Encoding::BINARY).to_sym is able to lookup the existing ISO-8859-1 symbol
535
+ # It likely is a JRuby bug.
536
+ expect(roundtrip(symbol).encoding).to be Encoding::ISO_8859_1
537
+ else
538
+ expect(roundtrip(symbol).encoding).to be Encoding::BINARY
539
+ end
295
540
  end
296
541
  end
297
542
 
@@ -315,7 +560,7 @@ describe MessagePack::Factory do
315
560
  before { subject.register_type(0x00, ::Symbol) }
316
561
 
317
562
  it 'lets symbols survive a roundtrip' do
318
- expect(symbol_after_roundtrip).to be :symbol
563
+ expect(roundtrip(:symbol)).to be :symbol
319
564
  end
320
565
 
321
566
  after do
@@ -342,6 +587,48 @@ describe MessagePack::Factory do
342
587
  GC.stress = false
343
588
  end
344
589
  end
590
+
591
+ it 'does not crash in recursive extensions' do
592
+ my_hash_type = Class.new(Hash)
593
+ factory = MessagePack::Factory.new
594
+ factory.register_type(7,
595
+ my_hash_type,
596
+ packer: ->(value, packer) do
597
+ packer.write(value.to_h)
598
+ end,
599
+ unpacker: ->(unpacker) { my_hash_type.new(unpacker.read) },
600
+ recursive: true,
601
+ )
602
+
603
+ payload = factory.dump(
604
+ [my_hash_type.new]
605
+ )
606
+
607
+ begin
608
+ GC.stress = true
609
+ factory.load(payload)
610
+ ensure
611
+ GC.stress = false
612
+ end
613
+ end
614
+ end
615
+
616
+ describe 'memsize' do
617
+ it 'works on a fresh factory' do
618
+ skip "JRuby doesn't support ObjectSpace.memsize_of" if IS_JRUBY
619
+
620
+ f = MessagePack::Factory.new
621
+ expect(ObjectSpace.memsize_of(f)).to be_an(Integer)
622
+ end
623
+
624
+ it 'works on a factory with registered types' do
625
+ skip "JRuby doesn't support ObjectSpace.memsize_of" if IS_JRUBY
626
+
627
+ f = MessagePack::Factory.new
628
+ base_size = ObjectSpace.memsize_of(f)
629
+ f.register_type(0x0a, Symbol)
630
+ expect(ObjectSpace.memsize_of(f)).to be > base_size
631
+ end
345
632
  end
346
633
 
347
634
  describe 'DefaultFactory' do
@@ -364,4 +651,56 @@ describe MessagePack::Factory do
364
651
  expect(MessagePack.unpack(MessagePack.pack(dm2))).to eq(dm2)
365
652
  end
366
653
  end
654
+
655
+ describe '#pool' do
656
+ let(:factory) { described_class.new }
657
+
658
+ it 'responds to serializers interface' do
659
+ pool = factory.pool(1)
660
+ expect(pool.load(pool.dump(42))).to be == 42
661
+ end
662
+
663
+ it 'types can be registered before the pool is created' do
664
+ factory.register_type(0x00, Symbol)
665
+ pool = factory.pool(1)
666
+ expect(pool.load(pool.dump(:foo))).to be == :foo
667
+ end
668
+
669
+ it 'types cannot be registered after the pool is created' do
670
+ pool = factory.pool(1)
671
+ factory.register_type(0x20, ::MyType)
672
+
673
+ expect do
674
+ pool.dump(MyType.new(1, 2))
675
+ end.to raise_error NoMethodError
676
+
677
+ payload = factory.dump(MyType.new(1, 2))
678
+ expect do
679
+ pool.load(payload)
680
+ end.to raise_error MessagePack::UnknownExtTypeError
681
+ end
682
+
683
+ it 'support symbolize_keys: true' do
684
+ pool = factory.pool(1, symbolize_keys: true)
685
+ expect(pool.load(pool.dump('foo' => 1))).to be == { foo: 1 }
686
+ end
687
+
688
+ it 'support freeze: true' do
689
+ pool = factory.pool(1, freeze: true)
690
+ expect(pool.load(pool.dump('foo'))).to be_frozen
691
+ end
692
+
693
+ it 'is thread safe' do
694
+ pool = factory.pool(1)
695
+
696
+ threads = 10.times.map do
697
+ Thread.new do
698
+ 1_000.times do |i|
699
+ expect(pool.load(pool.dump(i))).to be == i
700
+ end
701
+ end
702
+ end
703
+ threads.each(&:join)
704
+ end
705
+ end
367
706
  end
data/spec/msgpack_spec.rb CHANGED
@@ -115,7 +115,7 @@ describe MessagePack do
115
115
  expect { MessagePack.pack(self) }.to raise_error(NoMethodError, /^undefined method `to_msgpack'/)
116
116
  end
117
117
 
118
- it 'rasies an error on #unpack with garbage' do
118
+ it 'raises an error on #unpack with garbage' do
119
119
  skip "but nothing was raised. why?"
120
120
  expect { MessagePack.unpack('asdka;sd') }.to raise_error(MessagePack::UnpackError)
121
121
  end
data/spec/packer_spec.rb CHANGED
@@ -488,6 +488,24 @@ describe MessagePack::Packer do
488
488
  it { is_expected.to eq "\xC7\x0F\x01value_msgpacked" }
489
489
  end
490
490
 
491
+ shared_examples_for 'extension subclasses core type' do |klass|
492
+ before { stub_const('Value', Class.new(klass)) }
493
+ let(:object) { Value.new }
494
+ subject { packer.pack(object).to_s }
495
+
496
+ it "defaults to #{klass.name} packer if no extension is present" do
497
+ expect(subject).to eq(MessagePack.dump(klass.new))
498
+ end
499
+
500
+ it "uses core type extension for #{klass.name}" do
501
+ packer.register_type(0x01, Value, ->(_) { 'value_msgpacked' })
502
+ expect(subject).to eq("\xC7\x0F\x01value_msgpacked")
503
+ end
504
+ end
505
+ it_behaves_like 'extension subclasses core type', Hash
506
+ it_behaves_like 'extension subclasses core type', Array
507
+ it_behaves_like 'extension subclasses core type', String
508
+
491
509
  context 'when registering a type for symbols' do
492
510
  before { packer.register_type(0x00, ::Symbol, :to_msgpack_ext) }
493
511
 
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "set"
2
+ require "objspace"
1
3
 
2
4
  if ENV['SIMPLE_COV']
3
5
  require 'simplecov'
@@ -14,12 +16,44 @@ if ENV['GC_STRESS']
14
16
  end
15
17
 
16
18
  require 'msgpack'
19
+ require "msgpack/bigint"
17
20
 
18
- def java?
19
- /java/ =~ RUBY_PLATFORM
21
+ if GC.respond_to?(:verify_compaction_references)
22
+ # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
23
+ # move objects around, helping to find object movement bugs.
24
+ begin
25
+ GC.verify_compaction_references(double_heap: true, toward: :empty)
26
+ rescue NotImplementedError
27
+ # Some platforms don't support compaction
28
+ end
29
+ end
30
+
31
+ if GC.respond_to?(:auto_compact=)
32
+ GC.auto_compact = true
33
+ end
34
+
35
+ IS_JRUBY = RUBY_ENGINE == 'jruby'
36
+
37
+ IS_TRUFFLERUBY = RUBY_ENGINE == 'truffleruby'
38
+
39
+ # checking if Hash#[]= (rb_hash_aset) dedupes string keys
40
+ def automatic_string_keys_deduplication?
41
+ h = {}
42
+ x = {}
43
+ r = rand.to_s
44
+ h[%W(#{r}).join('')] = :foo
45
+ x[%W(#{r}).join('')] = :foo
46
+
47
+ x.keys[0].equal?(h.keys[0])
48
+ end
49
+
50
+ def string_deduplication?
51
+ r1 = rand.to_s
52
+ r2 = r1.dup
53
+ (-r1).equal?(-r2)
20
54
  end
21
55
 
22
- if java?
56
+ if IS_JRUBY
23
57
  RSpec.configure do |c|
24
58
  c.treat_symbols_as_metadata_keys_with_true_values = true
25
59
  c.filter_run_excluding :encodings => !(defined? Encoding)
@@ -60,6 +60,44 @@ describe MessagePack::Timestamp do
60
60
  it 'runs correctly (regression)' do
61
61
  expect(factory.unpack(factory.pack(Time.utc(2200)))).to eq(Time.utc(2200))
62
62
  end
63
+
64
+ let(:time32_max) { Time.new(2106, 2, 7, 6, 28, 15, "+00:00") }
65
+ it 'is serialized into timestamp32' do
66
+ expect(factory.pack(time32_max).size).to be 6
67
+ expect(factory.unpack(factory.pack(time32_max)).utc).to eq(time32_max)
68
+ end
69
+
70
+ let(:time64_min) { Time.new(2106, 2, 7, 6, 28, 16, "+00:00") }
71
+ it 'is serialized into timestamp64' do
72
+ expect(factory.pack(time64_min).size).to be 10
73
+ expect(factory.unpack(factory.pack(time64_min)).utc).to eq(time64_min)
74
+ end
75
+
76
+ let(:time64_max) { Time.at(Time.new(2514, 5, 30, 1, 53, 3, "+00:00").to_i, 999999999 / 1000.0r).utc } # TODO: use Time.at(sec, nsec, :nsec) when removing Ruby 2.4 from the list
77
+ it 'is serialized into timestamp64' do
78
+ expect(factory.pack(time64_max).size).to be 10
79
+ expect(factory.unpack(factory.pack(time64_max)).utc).to eq(time64_max)
80
+ end
81
+
82
+ let(:time96_positive_min) { Time.new(2514, 5, 30, 1, 53, 4, "+00:00") }
83
+ it 'is serialized into timestamp96' do
84
+ expect(factory.pack(time96_positive_min).size).to be 15
85
+ expect(factory.unpack(factory.pack(time96_positive_min)).utc).to eq(time96_positive_min)
86
+ end
87
+
88
+ let(:time96_min) { Time.at(-2**63).utc }
89
+ it 'is serialized into timestamp96' do
90
+ skip if IS_JRUBY || IS_TRUFFLERUBY # JRuby and TruffleRuby both use underlying Java time classes that do not support |year| >= 1 billion
91
+ expect(factory.pack(time96_min).size).to be 15
92
+ expect(factory.unpack(factory.pack(time96_min)).utc).to eq(time96_min)
93
+ end
94
+
95
+ let(:time96_max) { Time.at(2**63 - 1).utc }
96
+ it 'is serialized into timestamp96' do
97
+ skip if IS_JRUBY || IS_TRUFFLERUBY # JRuby and TruffleRuby both use underlying Java time classes that do not support |year| >= 1 billion
98
+ expect(factory.pack(time96_max).size).to be 15
99
+ expect(factory.unpack(factory.pack(time96_max)).utc).to eq(time96_max)
100
+ end
63
101
  end
64
102
 
65
103
  describe 'register_type with MessagePack::Timestamp' do