msgpack 1.3.3 → 1.6.0

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.
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