messagepack 1.0.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/README.adoc +773 -0
  3. data/Rakefile +8 -0
  4. data/docs/Gemfile +7 -0
  5. data/docs/README.md +85 -0
  6. data/docs/_config.yml +137 -0
  7. data/docs/_guides/index.adoc +14 -0
  8. data/docs/_guides/io-streaming.adoc +226 -0
  9. data/docs/_guides/migration.adoc +218 -0
  10. data/docs/_guides/performance.adoc +189 -0
  11. data/docs/_pages/buffer.adoc +85 -0
  12. data/docs/_pages/extension-types.adoc +117 -0
  13. data/docs/_pages/factory-pattern.adoc +115 -0
  14. data/docs/_pages/index.adoc +20 -0
  15. data/docs/_pages/serialization.adoc +159 -0
  16. data/docs/_pages/streaming.adoc +97 -0
  17. data/docs/_pages/symbol-extension.adoc +69 -0
  18. data/docs/_pages/timestamp-extension.adoc +88 -0
  19. data/docs/_references/api.adoc +360 -0
  20. data/docs/_references/extensions.adoc +198 -0
  21. data/docs/_references/format.adoc +301 -0
  22. data/docs/_references/index.adoc +14 -0
  23. data/docs/_tutorials/extension-types.adoc +170 -0
  24. data/docs/_tutorials/getting-started.adoc +165 -0
  25. data/docs/_tutorials/index.adoc +14 -0
  26. data/docs/_tutorials/thread-safety.adoc +157 -0
  27. data/docs/index.adoc +77 -0
  28. data/docs/lychee.toml +42 -0
  29. data/lib/messagepack/bigint.rb +131 -0
  30. data/lib/messagepack/buffer.rb +534 -0
  31. data/lib/messagepack/core_ext.rb +34 -0
  32. data/lib/messagepack/error.rb +24 -0
  33. data/lib/messagepack/extensions/base.rb +55 -0
  34. data/lib/messagepack/extensions/registry.rb +154 -0
  35. data/lib/messagepack/extensions/symbol.rb +38 -0
  36. data/lib/messagepack/extensions/timestamp.rb +110 -0
  37. data/lib/messagepack/extensions/value.rb +38 -0
  38. data/lib/messagepack/factory.rb +349 -0
  39. data/lib/messagepack/format.rb +99 -0
  40. data/lib/messagepack/packer.rb +702 -0
  41. data/lib/messagepack/symbol.rb +4 -0
  42. data/lib/messagepack/time.rb +29 -0
  43. data/lib/messagepack/timestamp.rb +4 -0
  44. data/lib/messagepack/unpacker.rb +1418 -0
  45. data/lib/messagepack/version.rb +5 -0
  46. data/lib/messagepack.rb +81 -0
  47. metadata +94 -0
@@ -0,0 +1,189 @@
1
+ ---
2
+ title: Performance optimization
3
+ nav_order: 1
4
+ ---
5
+
6
+ == Purpose
7
+
8
+ This guide covers techniques for maximizing MessagePack Ruby performance.
9
+
10
+ == References
11
+
12
+ * link:../pages/buffer[Buffer management] - Buffer optimization details
13
+ * link:../references/api[API reference] - Low-level API documentation
14
+
15
+ == Concepts
16
+
17
+ === Native type fast-path
18
+
19
+ Native MessagePack types bypass the extension registry:
20
+
21
+ * nil, boolean, integer, float, string, symbol, array, hash
22
+ * No O(n) registry search for these types
23
+ * Performance unaffected by number of registered extension types
24
+
25
+ === Buffer coalescing
26
+
27
+ Small writes are automatically merged:
28
+
29
+ * Writes < 512 bytes are coalesced into larger chunks
30
+ * Reduces memory allocations and GC pressure
31
+ * Significantly faster for patterns with many small writes
32
+
33
+ === Factory pools
34
+
35
+ Reuse packer/unpacker instances:
36
+
37
+ * Avoid allocation overhead
38
+ * Thread-safe concurrent access
39
+ * Configure pool size for your workload
40
+
41
+ == Optimization techniques
42
+
43
+ === Use native types when possible
44
+
45
+ [source,ruby]
46
+ ----
47
+ # Faster: Native hash
48
+ data = { name: "Alice", age: 30 }
49
+
50
+ # Slower: Custom class requires registry lookup
51
+ class Person
52
+ attr_accessor :name, :age
53
+ end
54
+ data = Person.new("Alice", 30)
55
+ ----
56
+
57
+ === Use factory pools for repeated operations
58
+
59
+ [source,ruby]
60
+ ----
61
+ # Create a pool for repeated use
62
+ pool = factory.pool(10)
63
+
64
+ # Fast: Reuses packer from pool
65
+ 1000.times do |i|
66
+ pool.pack(id: i, value: "data")
67
+ end
68
+ ----
69
+
70
+ === Use streaming for large data
71
+
72
+ [source,ruby]
73
+ ----
74
+ # Memory efficient for large files
75
+ buffer = Messagepack::BinaryBuffer.new(File.open("large.msgpack", "rb"))
76
+ unpacker = Messagepack::Unpacker.new(buffer)
77
+
78
+ while obj = unpacker.read
79
+ process(obj) # One object at a time
80
+ end
81
+ ----
82
+
83
+ === Minimize extension type overhead
84
+
85
+ [source,ruby]
86
+ ----
87
+ # Faster: Use native types in hash
88
+ data = { type: "user", id: 123 }
89
+
90
+ # Slower: Custom type class requires registry lookup
91
+ data = DataWrapper.new("user", 123)
92
+ ----
93
+
94
+ == Benchmarking
95
+
96
+ === Simple benchmark
97
+
98
+ [source,ruby]
99
+ ----
100
+ require 'benchmark'
101
+
102
+ data = { key: "value" * 100 }
103
+
104
+ n = 10000
105
+
106
+ Benchmark.bm do |x|
107
+ x.report("pack:") do
108
+ n.times { Messagepack.pack(data) }
109
+ end
110
+
111
+ x.report("unpack:") do
112
+ binary = Messagepack.pack(data)
113
+ n.times { Messagepack.unpack(binary) }
114
+ end
115
+ end
116
+ ----
117
+
118
+ === Comparing approaches
119
+
120
+ [source,ruby]
121
+ ----
122
+ # Compare factory vs pool
123
+ factory = Messagepack::Factory.new
124
+ pool = factory.pool(5)
125
+
126
+ Benchmark.bm do |x|
127
+ x.report("factory:") do
128
+ 1000.times { factory.pack(data) }
129
+ end
130
+
131
+ x.report("pool:") do
132
+ 1000.times { pool.pack(data) }
133
+ end
134
+ end
135
+ ----
136
+
137
+ == Performance characteristics
138
+
139
+ Typical performance on modern hardware:
140
+
141
+ * Packing simple values: 500k-700k ops/sec
142
+ * Packing small arrays: 200k-300k ops/sec
143
+ * Packing small hashes: 150k-200k ops/sec
144
+ * Buffer operations: ~30% faster with coalescing
145
+
146
+ Your results will vary based on:
147
+ * Data size and complexity
148
+ * Ruby implementation (MRI, JRuby, TruffleRuby)
149
+ * Hardware and OS
150
+ * Number of registered extension types
151
+
152
+ == Common pitfalls
153
+
154
+ === Creating new factories repeatedly
155
+
156
+ [source,ruby]
157
+ ----
158
+ # Bad: Creates new factory each time
159
+ def process(data)
160
+ factory = Messagepack::Factory.new
161
+ factory.pack(data)
162
+ end
163
+
164
+ # Good: Reuse factory
165
+ FACTORY = Messagepack::Factory.new.freeze
166
+
167
+ def process(data)
168
+ FACTORY.pack(data)
169
+ end
170
+ ----
171
+
172
+ === Not using pools in threads
173
+
174
+ [source,ruby]
175
+ ----
176
+ # Bad: Shared unpacker is not thread-safe
177
+ UNPACKER = Messagepack::Unpacker.new
178
+
179
+ threads.map do
180
+ Thread.new { UNPACKER.unpack(data) }
181
+ end
182
+
183
+ # Good: Use pool
184
+ pool = factory.pool(10)
185
+
186
+ threads.map do
187
+ Thread.new { pool.unpack(data) }
188
+ end
189
+ ----
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Buffer
3
+ parent: Core topics
4
+ nav_order: 6
5
+ ---
6
+
7
+ [[buffer]]
8
+ = Buffer management
9
+
10
+ == Purpose
11
+
12
+ The `BinaryBuffer` class provides efficient chunked storage for binary data.
13
+
14
+ == References
15
+
16
+ * link:../references/api[API reference] - BinaryBuffer class documentation
17
+ * link:../guides/performance[Performance guide] - Buffer optimization techniques
18
+
19
+ == Concepts
20
+
21
+ === Buffer operations
22
+
23
+ [source,ruby]
24
+ ----
25
+ buffer = Messagepack::BinaryBuffer.new
26
+ buffer << "data"
27
+ buffer.read(4) # => "data"
28
+ buffer.to_s # => ""
29
+ ----
30
+
31
+ Where,
32
+
33
+ * `BinaryBuffer.new` creates a new buffer
34
+ * `<<` appends data to the buffer
35
+ * `read(n)` reads and consumes n bytes
36
+ * `to_s` returns remaining data without consuming
37
+
38
+ === Skip operations
39
+
40
+ [source,ruby]
41
+ ----
42
+ buffer = Messagepack::BinaryBuffer.new
43
+ buffer << "\x81\xA3foo\xA5world"
44
+ buffer.skip # Skip one object (format byte)
45
+ buffer.skip_nil # Skip nil value if present
46
+ ----
47
+
48
+ Where,
49
+
50
+ * `skip` skips a complete MessagePack object
51
+ * `skip_nil` efficiently skips nil values
52
+
53
+ === Buffer with IO
54
+
55
+ [source,ruby]
56
+ ----
57
+ File.open("data.msgpack", "rb") do |io|
58
+ buffer = Messagepack::BinaryBuffer.new(io)
59
+ unpacker = Messagepack::Unpacker.new(buffer)
60
+ obj = unpacker.read
61
+ end
62
+ ----
63
+
64
+ Where,
65
+
66
+ * The buffer reads from the IO when needed
67
+ * Data is automatically managed in chunks
68
+ * Suitable for large files that don't fit in memory
69
+
70
+ == Examples
71
+
72
+ .Reading large MessagePack files efficiently
73
+ ====
74
+ [source,ruby]
75
+ ----
76
+ # Process a large file without loading everything into memory
77
+ buffer = Messagepack::BinaryBuffer.new(File.open("large.msgpack", "rb"))
78
+ unpacker = Messagepack::Unpacker.new(buffer)
79
+
80
+ while obj = unpacker.read
81
+ # Process each object one at a time
82
+ process(obj)
83
+ end
84
+ ----
85
+ ====
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Extension types
3
+ parent: Core topics
4
+ nav_order: 3
5
+ ---
6
+
7
+ [[extension-types]]
8
+ = Extension types
9
+
10
+ == Purpose
11
+
12
+ MessagePack supports custom extension types for serializing objects that don't
13
+ have a native MessagePack representation.
14
+
15
+ == References
16
+
17
+ * link:../references/extensions[Extension types reference] - Detailed extension documentation
18
+ * link:../tutorials/extension-types[Custom extension types tutorial] - Step-by-step guide
19
+
20
+ == Concepts
21
+
22
+ === Extension type format
23
+
24
+ [source,ruby]
25
+ ----
26
+ factory.register_type(type_id, class,
27
+ packer: packer_specification,
28
+ unpacker: unpacker_specification
29
+ )
30
+ ----
31
+
32
+ Where,
33
+
34
+ * `type_id` is an integer from -128 to 127
35
+ * `class` is the Ruby class to serialize
36
+ * `packer_specification` can be:
37
+ ** A symbol (method name to call on the object)
38
+ ** A proc (called with the object)
39
+ ** A method object
40
+ * `unpacker_specification` can be:
41
+ ** A symbol (class method to call)
42
+ ** A proc (called with the payload data)
43
+ ** A method object
44
+
45
+ === Recursive extension types
46
+
47
+ [source,ruby]
48
+ ----
49
+ factory.register_type(0x02, MyContainer,
50
+ packer: ->(obj, packer) { packer.write(obj.to_h) },
51
+ unpacker: ->(unpacker) { MyContainer.from_hash(unpacker.read) },
52
+ recursive: true
53
+ )
54
+ ----
55
+
56
+ Where,
57
+
58
+ * `recursive: true` enables nested serialization
59
+ * The `packer` lambda receives the packer instance for recursive calls
60
+ * The `unpacker` lambda receives the unpacker instance for recursive reads
61
+
62
+ == Examples
63
+
64
+ .Custom extension type for Money objects
65
+ ====
66
+ [source,ruby]
67
+ ----
68
+ class Money
69
+ attr_reader :amount, :currency
70
+
71
+ def initialize(amount, currency)
72
+ @amount = amount
73
+ @currency = currency
74
+ end
75
+
76
+ def to_msgpack_ext
77
+ [amount, currency].pack("QA*")
78
+ end
79
+
80
+ def self.from_msgpack_ext(data)
81
+ amount, currency = data.unpack("QA*")
82
+ new(amount, currency)
83
+ end
84
+ end
85
+
86
+ factory = Messagepack::Factory.new
87
+ factory.register_type(0x10, Money,
88
+ packer: :to_msgpack_ext,
89
+ unpacker: :from_msgpack_ext
90
+ )
91
+
92
+ money = Money.new(1000, "USD")
93
+ binary = factory.pack(money)
94
+ result = factory.unpack(binary)
95
+ # => #<Money:0x... @amount=1000, @currency="USD">
96
+ ----
97
+ ====
98
+
99
+ .Using procs for simple serialization
100
+ ====
101
+ [source,ruby]
102
+ ----
103
+ factory = Messagepack::Factory.new
104
+
105
+ # Serialize a URI object
106
+ require 'uri'
107
+ factory.register_type(0x20, URI,
108
+ packer: ->(uri) { uri.to_s },
109
+ unpacker: ->(str) { URI.parse(str) }
110
+ )
111
+
112
+ uri = URI.parse("https://example.com/path")
113
+ binary = factory.pack(uri)
114
+ result = factory.unpack(binary)
115
+ # => #<URI::HTTPS https://example.com/path>
116
+ ----
117
+ ====
@@ -0,0 +1,115 @@
1
+ ---
2
+ title: Factory pattern
3
+ parent: Core topics
4
+ nav_order: 2
5
+ ---
6
+
7
+ [[factory-pattern]]
8
+ = Factory pattern
9
+
10
+ == Purpose
11
+
12
+ The `Messagepack::Factory` class provides thread-safe management of packer and
13
+ unpacker instances with support for custom type registrations.
14
+
15
+ == References
16
+
17
+ * link:../references/api[API reference] - Factory class documentation
18
+ * link:../guides/thread-safety[Thread-safe usage tutorial] - Multi-threading best practices
19
+
20
+ == Concepts
21
+
22
+ === Creating a factory
23
+
24
+ [source,ruby]
25
+ ----
26
+ factory = Messagepack::Factory.new
27
+ ----
28
+
29
+ Where,
30
+
31
+ * `Factory.new` creates a new factory instance
32
+ * Each factory maintains its own type registry
33
+ * Factories can be frozen for thread-safe use
34
+
35
+ === Registering custom types
36
+
37
+ [source,ruby]
38
+ ----
39
+ factory.register_type(0x01, MyClass,
40
+ packer: :to_msgpack_ext,
41
+ unpacker: :from_msgpack_ext
42
+ )
43
+ ----
44
+
45
+ Where,
46
+
47
+ * `0x01` is the type identifier (must be -128 to 127)
48
+ * `MyClass` is the Ruby class to register
49
+ * `packer` specifies how to serialize instances (symbol, method, or proc)
50
+ * `unpacker` specifies how to deserialize data (symbol, method, or proc)
51
+
52
+ === Using factory pool for thread safety
53
+
54
+ [source,ruby]
55
+ ----
56
+ pool = factory.pool(5) # Create pool with 5 packers/unpackers
57
+ data = pool.pack(my_object) # Thread-safe packing
58
+ obj = pool.unpack(binary) # Thread-safe unpacking
59
+ ----
60
+
61
+ Where,
62
+
63
+ * `factory.pool(size)` creates a thread-safe pool
64
+ * `size` is the number of packer/unpacker instances in the pool
65
+ * The pool automatically manages instance reuse
66
+ * Each thread gets its own instance from the pool
67
+
68
+ == Examples
69
+
70
+ .Thread-safe factory usage
71
+ ====
72
+ [source,ruby]
73
+ ----
74
+ # Create a factory with custom types
75
+ factory = Messagepack::Factory.new
76
+ factory.register_type(0x01, MyCustomClass,
77
+ packer: ->(obj) { obj.serialize },
78
+ unpacker: ->(data) { MyCustomClass.deserialize(data) }
79
+ )
80
+
81
+ # Create a thread-safe pool
82
+ pool = factory.pool(10)
83
+
84
+ # Use from multiple threads safely
85
+ threads = 10.times.map do |i|
86
+ Thread.new do
87
+ object = MyCustomClass.new("data-#{i}")
88
+ binary = pool.pack(object)
89
+ result = pool.unpack(binary)
90
+ result.value
91
+ end
92
+ end
93
+
94
+ puts threads.map(&:value).inspect
95
+ ----
96
+ ====
97
+
98
+ .Pool size and performance
99
+ ====
100
+ [source,ruby]
101
+ ----
102
+ # Create factory
103
+ factory = Messagepack::Factory.new
104
+
105
+ # Pool size should match expected concurrent threads
106
+ # Too small: threads wait for available instances
107
+ # Too much: wasted memory
108
+
109
+ pool = factory.pool(4) # Match your thread pool size
110
+
111
+ # Default factory is also available
112
+ Messagepack.pack(data) # Uses DefaultFactory internally
113
+ Messagepack::DefaultFactory.register_type(0x01, MyClass, ...)
114
+ ----
115
+ ====
@@ -0,0 +1,20 @@
1
+ ---
2
+ title: Core topics
3
+ nav_order: 1
4
+ parent: Core topics
5
+ grand_parent: Core topics
6
+ ---
7
+
8
+ == Purpose
9
+
10
+ Core topics cover fundamental concepts and essential information for using the MessagePack Ruby library.
11
+
12
+ == Topics
13
+
14
+ * link:../serialization[Serialization] - Converting Ruby objects to binary format and back
15
+ * link:../factory-pattern[Factory pattern] - Thread-safe packer/unpacker management
16
+ * link:../extension-types[Extension types] - Custom type registration system
17
+ * link:../timestamp-extension[Timestamp extension] - Nanosecond precision time handling
18
+ * link:../symbol-extension[Symbol extension] - Efficient symbol serialization
19
+ * link:../buffer[Buffer management] - Efficient chunked binary data storage
20
+ * link:../streaming[Streaming] - Incremental data parsing
@@ -0,0 +1,159 @@
1
+ ---
2
+ title: Serialization
3
+ parent: Core topics
4
+ nav_order: 1
5
+ ---
6
+
7
+ [[serialization]]
8
+ = Serialization
9
+
10
+ == Purpose
11
+
12
+ The core MessagePack API provides simple `pack` and `unpack` methods for
13
+ serializing and deserializing Ruby objects to and from binary format.
14
+
15
+ == References
16
+
17
+ * link:../references/api[API reference] - Complete method documentation
18
+ * link:../references/format[Format specification] - Binary format details
19
+
20
+ == Concepts
21
+
22
+ === Packing objects
23
+
24
+ Use `Messagepack.pack` to serialize Ruby objects to binary format:
25
+
26
+ [source,ruby]
27
+ ----
28
+ Messagepack.pack({hello: "world"}) # => "\x81\xA5hello\xA5world"
29
+ ----
30
+
31
+ Where,
32
+
33
+ * `Messagepack.pack` accepts any Ruby object as its argument
34
+ * The return value is a binary string containing the serialized data
35
+ * Supported types include: nil, boolean, integer, float, string, array,
36
+ hash, and any registered extension types
37
+
38
+ === Unpacking data
39
+
40
+ Use `Messagepack.unpack` to deserialize binary data back to Ruby objects:
41
+
42
+ [source,ruby]
43
+ ----
44
+ data = Messagepack.pack({hello: "world"})
45
+ Messagepack.unpack(data) # => {"hello"=>"world"}
46
+ ----
47
+
48
+ Where,
49
+
50
+ * `Messagepack.unpack` accepts a binary string or IO object
51
+ * The return value is the original Ruby object
52
+ * Extra bytes after the deserialized object will raise a
53
+ `Messagepack::MalformedFormatError`
54
+
55
+ == Supported types
56
+
57
+ === Nil
58
+
59
+ [source,ruby]
60
+ ----
61
+ Messagepack.pack(nil) # => "\xC0"
62
+ Messagepack.unpack("\xC0") # => nil
63
+ ----
64
+
65
+ === Boolean
66
+
67
+ [source,ruby]
68
+ ----
69
+ Messagepack.pack(true) # => "\xC3"
70
+ Messagepack.pack(false) # => "\xC2"
71
+ Messagepack.unpack("\xC3") # => true
72
+ Messagepack.unpack("\xC2") # => false
73
+ ----
74
+
75
+ === Integer
76
+
77
+ MessagePack supports integers from -2^63 to 2^64-1:
78
+
79
+ [source,ruby]
80
+ ----
81
+ Messagepack.pack(0) # => "\x00"
82
+ Messagepack.pack(127) # => "\x7F"
83
+ Messagepack.pack(128) # => "\xCC\x80"
84
+ Messagepack.pack(-1) # => "\xFF"
85
+ Messagepack.unpack("\xCC\x80") # => 128
86
+ ----
87
+
88
+ === Float
89
+
90
+ [source,ruby]
91
+ ----
92
+ Messagepack.pack(3.14) # => "\xCB@\x09\x1E\xB8Q\xEB\x85\x1F"
93
+ Messagepack.unpack("\xCB@\x09\x1E\xB8Q\xEB\x85\x1F") # => 3.14
94
+ ----
95
+
96
+ === String
97
+
98
+ Strings are encoded as UTF-8:
99
+
100
+ [source,ruby]
101
+ ----
102
+ Messagepack.pack("hello") # => "\xA5hello"
103
+ Messagepack.unpack("\xA5hello") # => "hello"
104
+ ----
105
+
106
+ === Array
107
+
108
+ [source,ruby]
109
+ ----
110
+ Messagepack.pack([1, 2, 3]) # => "\x93\x01\x02\x03"
111
+ Messagepack.unpack("\x93\x01\x02\x03") # => [1, 2, 3]
112
+ ----
113
+
114
+ === Hash
115
+
116
+ [source,ruby]
117
+ ----
118
+ Messagepack.pack({a: 1, b: 2}) # => "\x82\xA1a\x01\xA1b\x02"
119
+ Messagepack.unpack("\x82\xA1a\x01\xA1b\x02") # => {"a"=>1, "b"=>2}
120
+ ----
121
+
122
+ == Examples
123
+
124
+ .Using pack and unpack
125
+ ====
126
+ [source,ruby]
127
+ ----
128
+ # Serialize a complex object
129
+ data = {
130
+ name: "Alice",
131
+ age: 30,
132
+ skills: ["Ruby", "Python"],
133
+ metadata: {
134
+ active: true,
135
+ score: 95.5
136
+ }
137
+ }
138
+
139
+ binary = Messagepack.pack(data)
140
+ # => "\x84\xA4name\xA5Alice\xA3age\x1E\xA6skills\
141
+ # \x92\xA4Ruby\xA6Python\xA8metadata\x82\xA6active\
142
+ # \xC3\xA5score\xCB@_\x00\x00"
143
+
144
+ # Deserialize back to a Ruby object
145
+ result = Messagepack.unpack(binary)
146
+ # => {"name"=>"Alice", "age"=>30, "skills"=>["Ruby", "Python"],
147
+ # "metadata"=>{"active"=>true, "score"=>95.5}}
148
+ ----
149
+ ====
150
+
151
+ == Aliases
152
+
153
+ The module also provides `dump` and `load` as aliases:
154
+
155
+ [source,ruby]
156
+ ----
157
+ Messagepack.dump(data) # Same as Messagepack.pack(data)
158
+ Messagepack.load(data) # Same as Messagepack.unpack(data)
159
+ ----