msgpack 1.2.9 → 1.4.4

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +56 -0
  3. data/.gitignore +3 -1
  4. data/.rubocop.yml +4 -1
  5. data/ChangeLog +46 -0
  6. data/Gemfile +3 -0
  7. data/README.md +242 -0
  8. data/Rakefile +1 -9
  9. data/doclib/msgpack/factory.rb +1 -0
  10. data/doclib/msgpack/time.rb +22 -0
  11. data/doclib/msgpack/timestamp.rb +44 -0
  12. data/ext/java/org/msgpack/jruby/Buffer.java +17 -16
  13. data/ext/java/org/msgpack/jruby/Decoder.java +29 -10
  14. data/ext/java/org/msgpack/jruby/Encoder.java +22 -12
  15. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +9 -9
  16. data/ext/java/org/msgpack/jruby/ExtensionValue.java +5 -8
  17. data/ext/java/org/msgpack/jruby/Factory.java +7 -2
  18. data/ext/java/org/msgpack/jruby/Packer.java +11 -9
  19. data/ext/java/org/msgpack/jruby/Unpacker.java +40 -27
  20. data/ext/msgpack/buffer.c +4 -16
  21. data/ext/msgpack/buffer.h +60 -5
  22. data/ext/msgpack/compat.h +1 -12
  23. data/ext/msgpack/extconf.rb +39 -7
  24. data/ext/msgpack/factory_class.c +10 -5
  25. data/ext/msgpack/packer.c +18 -5
  26. data/ext/msgpack/packer.h +0 -16
  27. data/ext/msgpack/packer_class.c +0 -9
  28. data/ext/msgpack/packer_ext_registry.c +0 -22
  29. data/ext/msgpack/unpacker.c +41 -49
  30. data/ext/msgpack/unpacker.h +8 -0
  31. data/ext/msgpack/unpacker_class.c +23 -13
  32. data/lib/msgpack/symbol.rb +14 -4
  33. data/lib/msgpack/time.rb +29 -0
  34. data/lib/msgpack/timestamp.rb +76 -0
  35. data/lib/msgpack/version.rb +4 -7
  36. data/lib/msgpack.rb +5 -8
  37. data/msgpack.gemspec +3 -7
  38. data/spec/factory_spec.rb +17 -0
  39. data/spec/msgpack_spec.rb +1 -1
  40. data/spec/packer_spec.rb +18 -0
  41. data/spec/spec_helper.rb +27 -0
  42. data/spec/timestamp_spec.rb +161 -0
  43. data/spec/unpacker_spec.rb +113 -1
  44. metadata +19 -51
  45. data/.travis.yml +0 -41
  46. data/README.rdoc +0 -209
@@ -77,9 +77,6 @@ VALUE MessagePack_Unpacker_initialize(int argc, VALUE* argv, VALUE self)
77
77
  VALUE v = argv[0];
78
78
  if(rb_type(v) == T_HASH) {
79
79
  options = v;
80
- if(rb_type(options) != T_HASH) {
81
- rb_raise(rb_eArgError, "expected Hash but found %s.", rb_obj_classname(options));
82
- }
83
80
  } else {
84
81
  io = v;
85
82
  }
@@ -108,6 +105,9 @@ VALUE MessagePack_Unpacker_initialize(int argc, VALUE* argv, VALUE self)
108
105
  v = rb_hash_aref(options, ID2SYM(rb_intern("symbolize_keys")));
109
106
  msgpack_unpacker_set_symbolized_keys(uk, RTEST(v));
110
107
 
108
+ v = rb_hash_aref(options, ID2SYM(rb_intern("freeze")));
109
+ msgpack_unpacker_set_freeze(uk, RTEST(v));
110
+
111
111
  v = rb_hash_aref(options, ID2SYM(rb_intern("allow_unknown_ext")));
112
112
  msgpack_unpacker_set_allow_unknown_ext(uk, RTEST(v));
113
113
  }
@@ -121,6 +121,12 @@ static VALUE Unpacker_symbolized_keys_p(VALUE self)
121
121
  return uk->symbolize_keys ? Qtrue : Qfalse;
122
122
  }
123
123
 
124
+ static VALUE Unpacker_freeze_p(VALUE self)
125
+ {
126
+ UNPACKER(self, uk);
127
+ return uk->freeze ? Qtrue : Qfalse;
128
+ }
129
+
124
130
  static VALUE Unpacker_allow_unknown_ext_p(VALUE self)
125
131
  {
126
132
  UNPACKER(self, uk);
@@ -256,6 +262,17 @@ static VALUE Unpacker_feed(VALUE self, VALUE data)
256
262
  return self;
257
263
  }
258
264
 
265
+ static VALUE Unpacker_feed_reference(VALUE self, VALUE data)
266
+ {
267
+ UNPACKER(self, uk);
268
+
269
+ StringValue(data);
270
+
271
+ msgpack_buffer_append_string_reference(UNPACKER_BUFFER_(uk), data);
272
+
273
+ return self;
274
+ }
275
+
259
276
  static VALUE Unpacker_each_impl(VALUE self)
260
277
  {
261
278
  UNPACKER(self, uk);
@@ -312,8 +329,7 @@ static VALUE Unpacker_feed_each(VALUE self, VALUE data)
312
329
  }
313
330
  #endif
314
331
 
315
- // TODO optimize
316
- Unpacker_feed(self, data);
332
+ Unpacker_feed_reference(self, data);
317
333
  return Unpacker_each(self);
318
334
  }
319
335
 
@@ -353,12 +369,7 @@ static VALUE Unpacker_register_type(int argc, VALUE* argv, VALUE self)
353
369
  case 1:
354
370
  /* register_type(0x7f) {|data| block... } */
355
371
  rb_need_block();
356
- #ifdef HAVE_RB_BLOCK_LAMBDA
357
372
  proc = rb_block_lambda();
358
- #else
359
- /* MRI 1.8 */
360
- proc = rb_block_proc();
361
- #endif
362
373
  arg = proc;
363
374
  ext_module = Qnil;
364
375
  break;
@@ -386,9 +397,6 @@ static VALUE Unpacker_full_unpack(VALUE self)
386
397
  {
387
398
  UNPACKER(self, uk);
388
399
 
389
- /* prefer reference than copying; see MessagePack_Unpacker_module_init */
390
- msgpack_buffer_set_write_reference_threshold(UNPACKER_BUFFER_(uk), 0);
391
-
392
400
  int r = msgpack_unpacker_read(uk, 0);
393
401
  if(r < 0) {
394
402
  raise_unpacker_error(r);
@@ -434,6 +442,7 @@ void MessagePack_Unpacker_module_init(VALUE mMessagePack)
434
442
 
435
443
  rb_define_method(cMessagePack_Unpacker, "initialize", MessagePack_Unpacker_initialize, -1);
436
444
  rb_define_method(cMessagePack_Unpacker, "symbolize_keys?", Unpacker_symbolized_keys_p, 0);
445
+ rb_define_method(cMessagePack_Unpacker, "freeze?", Unpacker_freeze_p, 0);
437
446
  rb_define_method(cMessagePack_Unpacker, "allow_unknown_ext?", Unpacker_allow_unknown_ext_p, 0);
438
447
  rb_define_method(cMessagePack_Unpacker, "buffer", Unpacker_buffer, 0);
439
448
  rb_define_method(cMessagePack_Unpacker, "read", Unpacker_read, 0);
@@ -444,6 +453,7 @@ void MessagePack_Unpacker_module_init(VALUE mMessagePack)
444
453
  rb_define_method(cMessagePack_Unpacker, "read_map_header", Unpacker_read_map_header, 0);
445
454
  //rb_define_method(cMessagePack_Unpacker, "peek_next_type", Unpacker_peek_next_type, 0); // TODO
446
455
  rb_define_method(cMessagePack_Unpacker, "feed", Unpacker_feed, 1);
456
+ rb_define_method(cMessagePack_Unpacker, "feed_reference", Unpacker_feed_reference, 1);
447
457
  rb_define_method(cMessagePack_Unpacker, "each", Unpacker_each, 0);
448
458
  rb_define_method(cMessagePack_Unpacker, "feed_each", Unpacker_feed_each, 1);
449
459
  rb_define_method(cMessagePack_Unpacker, "reset", Unpacker_reset, 0);
@@ -1,9 +1,19 @@
1
1
  class Symbol
2
- def to_msgpack_ext
3
- [to_s].pack('A*')
2
+ # to_msgpack_ext is supposed to return a binary string.
3
+ # The canonical way to do it for symbols would be:
4
+ # [to_s].pack('A*')
5
+ # However in this instance we can take a shortcut
6
+ if method_defined?(:name)
7
+ alias_method :to_msgpack_ext, :name
8
+ else
9
+ alias_method :to_msgpack_ext, :to_s
4
10
  end
5
11
 
6
12
  def self.from_msgpack_ext(data)
7
- data.unpack('A*').first.to_sym
13
+ # from_msgpack_ext is supposed to parse a binary string.
14
+ # The canonical way to do it for symbols would be:
15
+ # data.unpack1('A*').to_sym
16
+ # However in this instance we can take a shortcut
17
+ data.to_sym
8
18
  end
9
- end
19
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # MessagePack extention packer and unpacker for built-in Time class
4
+ module MessagePack
5
+ module Time
6
+ # 3-arg Time.at is available Ruby >= 2.5
7
+ TIME_AT_3_AVAILABLE = begin
8
+ !!::Time.at(0, 0, :nanosecond)
9
+ rescue ArgumentError
10
+ false
11
+ end
12
+
13
+ Unpacker = if TIME_AT_3_AVAILABLE
14
+ lambda do |payload|
15
+ tv = MessagePack::Timestamp.from_msgpack_ext(payload)
16
+ ::Time.at(tv.sec, tv.nsec, :nanosecond)
17
+ end
18
+ else
19
+ lambda do |payload|
20
+ tv = MessagePack::Timestamp.from_msgpack_ext(payload)
21
+ ::Time.at(tv.sec, tv.nsec / 1000.0r)
22
+ end
23
+ end
24
+
25
+ Packer = lambda { |time|
26
+ MessagePack::Timestamp.to_msgpack_ext(time.tv_sec, time.tv_nsec)
27
+ }
28
+ end
29
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessagePack
4
+ class Timestamp # a.k.a. "TimeSpec"
5
+ # Because the byte-order of MessagePack is big-endian in,
6
+ # pack() and unpack() specifies ">".
7
+ # See https://docs.ruby-lang.org/en/trunk/Array.html#method-i-pack for details.
8
+
9
+ # The timestamp extension type defined in the MessagePack spec.
10
+ # See https://github.com/msgpack/msgpack/blob/master/spec.md#timestamp-extension-type for details.
11
+ TYPE = -1
12
+
13
+ TIMESTAMP32_MAX_SEC = (1 << 32) - 1
14
+ TIMESTAMP64_MAX_SEC = (1 << 34) - 1
15
+
16
+ # @return [Integer]
17
+ attr_reader :sec
18
+
19
+ # @return [Integer]
20
+ attr_reader :nsec
21
+
22
+ # @param [Integer] sec
23
+ # @param [Integer] nsec
24
+ def initialize(sec, nsec)
25
+ @sec = sec
26
+ @nsec = nsec
27
+ end
28
+
29
+ def self.from_msgpack_ext(data)
30
+ case data.length
31
+ when 4
32
+ # timestamp32 (sec: uint32be)
33
+ sec, = data.unpack('L>')
34
+ new(sec, 0)
35
+ when 8
36
+ # timestamp64 (nsec: uint30be, sec: uint34be)
37
+ n, s = data.unpack('L>2')
38
+ sec = ((n & 0b11) << 32) | s
39
+ nsec = n >> 2
40
+ new(sec, nsec)
41
+ when 12
42
+ # timestam96 (nsec: uint32be, sec: int64be)
43
+ nsec, sec = data.unpack('L>q>')
44
+ new(sec, nsec)
45
+ else
46
+ raise MalformedFormatError, "Invalid timestamp data size: #{data.length}"
47
+ end
48
+ end
49
+
50
+ def self.to_msgpack_ext(sec, nsec)
51
+ if sec >= 0 && nsec >= 0 && sec <= TIMESTAMP64_MAX_SEC
52
+ if nsec === 0 && sec <= TIMESTAMP32_MAX_SEC
53
+ # timestamp32 = (sec: uint32be)
54
+ [sec].pack('L>')
55
+ else
56
+ # timestamp64 (nsec: uint30be, sec: uint34be)
57
+ nsec30 = nsec << 2
58
+ sec_high2 = sec >> 32 # high 2 bits (`x & 0b11` is redandunt)
59
+ sec_low32 = sec & 0xffffffff # low 32 bits
60
+ [nsec30 | sec_high2, sec_low32].pack('L>2')
61
+ end
62
+ else
63
+ # timestamp96 (nsec: uint32be, sec: int64be)
64
+ [nsec, sec].pack('L>q>')
65
+ end
66
+ end
67
+
68
+ def to_msgpack_ext
69
+ self.class.to_msgpack_ext(sec, nsec)
70
+ end
71
+
72
+ def ==(other)
73
+ other.class == self.class && sec == other.sec && nsec == other.nsec
74
+ end
75
+ end
76
+ end
@@ -1,9 +1,6 @@
1
1
  module MessagePack
2
- VERSION = "1.2.9"
3
-
4
- # NOTE for msgpack-ruby maintainer:
5
- # Check these things to release new binaryes for new Ruby versions (especially for Windows):
6
- # * versions/supports of rake-compiler & rake-compiler-dock
7
- # * update RUBY_CC_VERSION in Rakefile
8
- # * check Ruby dependency of released mswin gem details
2
+ VERSION = "1.4.4"
3
+ # Note for maintainers:
4
+ # Don't miss building/releasing the JRuby version (rake buld:java)
5
+ # See "How to build -java rubygems" in README for more details.
9
6
  end
data/lib/msgpack.rb CHANGED
@@ -1,15 +1,10 @@
1
1
  require "msgpack/version"
2
2
 
3
3
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby" # This is same with `/java/ =~ RUBY_VERSION`
4
- require "java"
5
4
  require "msgpack/msgpack.jar"
6
- org.msgpack.jruby.MessagePackLibrary.new.load(JRuby.runtime, false)
5
+ JRuby::Util.load_ext("org.msgpack.jruby.MessagePackLibrary")
7
6
  else
8
- begin
9
- require "msgpack/#{RUBY_VERSION[/\d+.\d+/]}/msgpack"
10
- rescue LoadError
11
- require "msgpack/msgpack"
12
- end
7
+ require "msgpack/msgpack"
13
8
  end
14
9
 
15
10
  require "msgpack/packer"
@@ -17,6 +12,8 @@ require "msgpack/unpacker"
17
12
  require "msgpack/factory"
18
13
  require "msgpack/symbol"
19
14
  require "msgpack/core_ext"
15
+ require "msgpack/timestamp"
16
+ require "msgpack/time"
20
17
 
21
18
  module MessagePack
22
19
  DefaultFactory = MessagePack::Factory.new
@@ -27,7 +24,7 @@ module MessagePack
27
24
 
28
25
  if src.is_a? String
29
26
  unpacker = DefaultFactory.unpacker param || DEFAULT_EMPTY_PARAMS
30
- unpacker.feed src
27
+ unpacker.feed_reference src
31
28
  else
32
29
  unpacker = DefaultFactory.unpacker src, param || DEFAULT_EMPTY_PARAMS
33
30
  end
data/msgpack.gemspec CHANGED
@@ -10,7 +10,6 @@ Gem::Specification.new do |s|
10
10
  s.email = ["frsyuki@gmail.com", "theo@iconara.net", "tagomoris@gmail.com"]
11
11
  s.license = "Apache 2.0"
12
12
  s.homepage = "http://msgpack.org/"
13
- s.rubyforge_project = "msgpack"
14
13
  s.require_paths = ["lib"]
15
14
  if /java/ =~ RUBY_PLATFORM
16
15
  s.files = Dir['lib/**/*.rb', 'lib/**/*.jar']
@@ -19,15 +18,12 @@ Gem::Specification.new do |s|
19
18
  s.files = `git ls-files`.split("\n")
20
19
  s.extensions = ["ext/msgpack/extconf.rb"]
21
20
  end
22
- s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
21
+
22
+ s.required_ruby_version = ">= 2.4"
23
23
 
24
24
  s.add_development_dependency 'bundler'
25
25
  s.add_development_dependency 'rake'
26
- s.add_development_dependency 'rake-compiler', ['~> 1.0']
27
- if /java/ !~ RUBY_PLATFORM
28
- # NOTE: rake-compiler-dock SHOULD be updated for new Ruby versions
29
- s.add_development_dependency 'rake-compiler-dock', ['~> 0.7.0']
30
- end
26
+ s.add_development_dependency 'rake-compiler', ['>= 1.1.9']
31
27
  s.add_development_dependency 'rspec', ['~> 3.3']
32
28
  s.add_development_dependency 'yard'
33
29
  s.add_development_dependency 'json'
data/spec/factory_spec.rb CHANGED
@@ -280,6 +280,23 @@ describe MessagePack::Factory do
280
280
  unpacker.feed(packed_symbol).unpack
281
281
  end
282
282
 
283
+ context 'using the optimized symbol unpacker' do
284
+ before do
285
+ skip if IS_JRUBY # JRuby implementation doesn't support the optimized symbols unpacker for now
286
+ subject.register_type(
287
+ 0x00,
288
+ ::Symbol,
289
+ packer: :to_msgpack_ext,
290
+ unpacker: :from_msgpack_ext,
291
+ optimized_symbols_parsing: true,
292
+ )
293
+ end
294
+
295
+ it 'lets symbols survive a roundtrip' do
296
+ expect(symbol_after_roundtrip).to be :symbol
297
+ end
298
+ end
299
+
283
300
  context 'if no ext type is registered for symbols' do
284
301
  it 'converts symbols to string' do
285
302
  expect(symbol_after_roundtrip).to eq 'symbol'
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
@@ -15,10 +15,37 @@ end
15
15
 
16
16
  require 'msgpack'
17
17
 
18
+ if GC.respond_to?(:verify_compaction_references)
19
+ # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
20
+ # move objects around, helping to find object movement bugs.
21
+ GC.verify_compaction_references(double_heap: true, toward: :empty)
22
+ end
23
+
24
+ if GC.respond_to?(:auto_compact=)
25
+ GC.auto_compact = true
26
+ end
27
+
18
28
  def java?
19
29
  /java/ =~ RUBY_PLATFORM
20
30
  end
21
31
 
32
+ # checking if Hash#[]= (rb_hash_aset) dedupes string keys
33
+ def automatic_string_keys_deduplication?
34
+ h = {}
35
+ x = {}
36
+ r = rand.to_s
37
+ h[%W(#{r}).join('')] = :foo
38
+ x[%W(#{r}).join('')] = :foo
39
+
40
+ x.keys[0].equal?(h.keys[0])
41
+ end
42
+
43
+ def string_deduplication?
44
+ r1 = rand.to_s
45
+ r2 = r1.dup
46
+ (-r1).equal?(-r2)
47
+ end
48
+
22
49
  if java?
23
50
  RSpec.configure do |c|
24
51
  c.treat_symbols_as_metadata_keys_with_true_values = true
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ IS_JRUBY = Kernel.const_defined?(:JRUBY_VERSION)
6
+
7
+ describe MessagePack::Timestamp do
8
+ describe 'malformed format' do
9
+ it do
10
+ expect do
11
+ MessagePack::Timestamp.from_msgpack_ext([0xd4, 0x00].pack("C*"))
12
+ end.to raise_error(MessagePack::MalformedFormatError)
13
+ end
14
+ end
15
+
16
+ describe 'register_type with Time' do
17
+ let(:factory) do
18
+ factory = MessagePack::Factory.new
19
+ factory.register_type(
20
+ MessagePack::Timestamp::TYPE,
21
+ Time,
22
+ packer: MessagePack::Time::Packer,
23
+ unpacker: MessagePack::Time::Unpacker
24
+ )
25
+ factory
26
+ end
27
+
28
+ let(:time) { Time.local(2019, 6, 17, 1, 2, 3, 123_456_789 / 1000.0) }
29
+ it 'serializes and deserializes Time' do
30
+ prefix_fixext8_with_type_id = [0xd7, -1].pack("c*")
31
+
32
+ packed = factory.pack(time)
33
+ expect(packed).to start_with(prefix_fixext8_with_type_id)
34
+ expect(packed.size).to eq(10)
35
+ unpacked = factory.unpack(packed)
36
+ expect(unpacked.to_i).to eq(time.to_i)
37
+ expect(unpacked.to_f).to eq(time.to_f)
38
+ # expect(unpacked).to eq(time) # we can't do it because of nsec (rational vs float?)
39
+ end
40
+
41
+ let(:time_without_nsec) { Time.local(2019, 6, 17, 1, 2, 3, 0) }
42
+ it 'serializes time without nanosec as fixext4' do
43
+ prefix_fixext4_with_type_id = [0xd6, -1].pack("c*")
44
+
45
+ packed = factory.pack(time_without_nsec)
46
+ expect(packed).to start_with(prefix_fixext4_with_type_id)
47
+ expect(packed.size).to eq(6)
48
+ unpacked = factory.unpack(packed)
49
+ expect(unpacked).to eq(time_without_nsec)
50
+ end
51
+
52
+ let(:time_after_2514) { Time.at(1 << 34) } # the max num of 34bit int means 2514-05-30 01:53:04 UTC
53
+ it 'serializes time after 2038 as ext8' do
54
+ prefix_ext8_with_12bytes_payload_and_type_id = [0xc7, 12, -1].pack("c*")
55
+
56
+ expect(time_after_2514.to_i).to be > 0xffffffff
57
+ packed = factory.pack(time_after_2514)
58
+ expect(packed).to start_with(prefix_ext8_with_12bytes_payload_and_type_id)
59
+ expect(packed.size).to eq(15)
60
+ end
61
+
62
+ it 'runs correctly (regression)' do
63
+ expect(factory.unpack(factory.pack(Time.utc(2200)))).to eq(Time.utc(2200))
64
+ end
65
+
66
+ let(:time32_max) { Time.new(2106, 2, 7, 6, 28, 15, "+00:00") }
67
+ it 'is serialized into timestamp32' do
68
+ expect(factory.pack(time32_max).size).to be 6
69
+ expect(factory.unpack(factory.pack(time32_max)).utc).to eq(time32_max)
70
+ end
71
+
72
+ let(:time64_min) { Time.new(2106, 2, 7, 6, 28, 16, "+00:00") }
73
+ it 'is serialized into timestamp64' do
74
+ expect(factory.pack(time64_min).size).to be 10
75
+ expect(factory.unpack(factory.pack(time64_min)).utc).to eq(time64_min)
76
+ end
77
+
78
+ 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
79
+ it 'is serialized into timestamp64' do
80
+ expect(factory.pack(time64_max).size).to be 10
81
+ expect(factory.unpack(factory.pack(time64_max)).utc).to eq(time64_max)
82
+ end
83
+
84
+ let(:time96_positive_min) { Time.new(2514, 5, 30, 1, 53, 4, "+00:00") }
85
+ it 'is serialized into timestamp96' do
86
+ expect(factory.pack(time96_positive_min).size).to be 15
87
+ expect(factory.unpack(factory.pack(time96_positive_min)).utc).to eq(time96_positive_min)
88
+ end
89
+
90
+ let(:time96_min) { Time.at(-2**63).utc }
91
+ it 'is serialized into timestamp96' do
92
+ skip if IS_JRUBY # JRuby cannot handle numerics larger than long
93
+ expect(factory.pack(time96_min).size).to be 15
94
+ expect(factory.unpack(factory.pack(time96_min)).utc).to eq(time96_min)
95
+ end
96
+
97
+ let(:time96_max) { Time.at(2**63 - 1).utc }
98
+ it 'is serialized into timestamp96' do
99
+ skip if IS_JRUBY # JRuby cannot handle numerics larger than long
100
+ expect(factory.pack(time96_max).size).to be 15
101
+ expect(factory.unpack(factory.pack(time96_max)).utc).to eq(time96_max)
102
+ end
103
+ end
104
+
105
+ describe 'register_type with MessagePack::Timestamp' do
106
+ let(:factory) do
107
+ factory = MessagePack::Factory.new
108
+ factory.register_type(MessagePack::Timestamp::TYPE, MessagePack::Timestamp)
109
+ factory
110
+ end
111
+
112
+ let(:timestamp) { MessagePack::Timestamp.new(Time.now.tv_sec, 123_456_789) }
113
+ it 'serializes and deserializes MessagePack::Timestamp' do
114
+ packed = factory.pack(timestamp)
115
+ unpacked = factory.unpack(packed)
116
+ expect(unpacked).to eq(timestamp)
117
+ end
118
+ end
119
+
120
+ describe 'timestamp32' do
121
+ it 'handles [1, 0]' do
122
+ t = MessagePack::Timestamp.new(1, 0)
123
+
124
+ payload = t.to_msgpack_ext
125
+ unpacked = MessagePack::Timestamp.from_msgpack_ext(payload)
126
+
127
+ expect(unpacked).to eq(t)
128
+ end
129
+ end
130
+
131
+ describe 'timestamp64' do
132
+ it 'handles [1, 1]' do
133
+ t = MessagePack::Timestamp.new(1, 1)
134
+
135
+ payload = t.to_msgpack_ext
136
+ unpacked = MessagePack::Timestamp.from_msgpack_ext(payload)
137
+
138
+ expect(unpacked).to eq(t)
139
+ end
140
+ end
141
+
142
+ describe 'timestamp96' do
143
+ it 'handles [-1, 0]' do
144
+ t = MessagePack::Timestamp.new(-1, 0)
145
+
146
+ payload = t.to_msgpack_ext
147
+ unpacked = MessagePack::Timestamp.from_msgpack_ext(payload)
148
+
149
+ expect(unpacked).to eq(t)
150
+ end
151
+
152
+ it 'handles [-1, 999_999_999]' do
153
+ t = MessagePack::Timestamp.new(-1, 999_999_999)
154
+
155
+ payload = t.to_msgpack_ext
156
+ unpacked = MessagePack::Timestamp.from_msgpack_ext(payload)
157
+
158
+ expect(unpacked).to eq(t)
159
+ end
160
+ end
161
+ end