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.
- checksums.yaml +7 -0
- data/README.adoc +773 -0
- data/Rakefile +8 -0
- data/docs/Gemfile +7 -0
- data/docs/README.md +85 -0
- data/docs/_config.yml +137 -0
- data/docs/_guides/index.adoc +14 -0
- data/docs/_guides/io-streaming.adoc +226 -0
- data/docs/_guides/migration.adoc +218 -0
- data/docs/_guides/performance.adoc +189 -0
- data/docs/_pages/buffer.adoc +85 -0
- data/docs/_pages/extension-types.adoc +117 -0
- data/docs/_pages/factory-pattern.adoc +115 -0
- data/docs/_pages/index.adoc +20 -0
- data/docs/_pages/serialization.adoc +159 -0
- data/docs/_pages/streaming.adoc +97 -0
- data/docs/_pages/symbol-extension.adoc +69 -0
- data/docs/_pages/timestamp-extension.adoc +88 -0
- data/docs/_references/api.adoc +360 -0
- data/docs/_references/extensions.adoc +198 -0
- data/docs/_references/format.adoc +301 -0
- data/docs/_references/index.adoc +14 -0
- data/docs/_tutorials/extension-types.adoc +170 -0
- data/docs/_tutorials/getting-started.adoc +165 -0
- data/docs/_tutorials/index.adoc +14 -0
- data/docs/_tutorials/thread-safety.adoc +157 -0
- data/docs/index.adoc +77 -0
- data/docs/lychee.toml +42 -0
- data/lib/messagepack/bigint.rb +131 -0
- data/lib/messagepack/buffer.rb +534 -0
- data/lib/messagepack/core_ext.rb +34 -0
- data/lib/messagepack/error.rb +24 -0
- data/lib/messagepack/extensions/base.rb +55 -0
- data/lib/messagepack/extensions/registry.rb +154 -0
- data/lib/messagepack/extensions/symbol.rb +38 -0
- data/lib/messagepack/extensions/timestamp.rb +110 -0
- data/lib/messagepack/extensions/value.rb +38 -0
- data/lib/messagepack/factory.rb +349 -0
- data/lib/messagepack/format.rb +99 -0
- data/lib/messagepack/packer.rb +702 -0
- data/lib/messagepack/symbol.rb +4 -0
- data/lib/messagepack/time.rb +29 -0
- data/lib/messagepack/timestamp.rb +4 -0
- data/lib/messagepack/unpacker.rb +1418 -0
- data/lib/messagepack/version.rb +5 -0
- data/lib/messagepack.rb +81 -0
- 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
|
+
----
|