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,97 @@
1
+ ---
2
+ title: Streaming
3
+ parent: Core topics
4
+ nav_order: 7
5
+ ---
6
+
7
+ [[streaming]]
8
+ = Streaming unpacking
9
+
10
+ == Purpose
11
+
12
+ The streaming unpacker allows incremental parsing of MessagePack data as it
13
+ becomes available.
14
+
15
+ == References
16
+
17
+ * link:../guides/io-streaming[IO streaming guide] - Working with streams
18
+ * link:../references/api[API reference] - Unpacker class documentation
19
+
20
+ == Concepts
21
+
22
+ === Feeding data incrementally
23
+
24
+ [source,ruby]
25
+ ----
26
+ unpacker = Messagepack::Unpacker.new
27
+ unpacker.feed("\x81") # Feed partial data
28
+ unpacker.feed("\xA3") # Feed more
29
+ unpacker.feed("foo") # Feed final part
30
+ obj = unpacker.read # => {"foo"=>nil}
31
+ ----
32
+
33
+ Where,
34
+
35
+ * `Unpacker.new` creates a new unpacker instance
36
+ * `feed(data)` appends data to the buffer
37
+ * `read` returns one complete object or `nil` if more data is needed
38
+
39
+ === Streaming from IO
40
+
41
+ [source,ruby]
42
+ ----
43
+ unpacker = Messagepack::Unpacker.new(io)
44
+ obj = unpacker.read # Reads from IO as needed
45
+ ----
46
+
47
+ Where,
48
+
49
+ * `Unpacker.new(io)` creates an unpacker attached to an IO
50
+ * The unpacker automatically reads from the IO when needed
51
+ * Use `full_unpack` to read a single object and reset
52
+
53
+ === Iterating over multiple objects
54
+
55
+ [source,ruby]
56
+ ----
57
+ unpacker = Messagepack::Unpacker.new
58
+ unpacker.feed(data1)
59
+ unpacker.feed(data2)
60
+ unpacker.feed_each { |obj| process(obj) }
61
+ ----
62
+
63
+ Where,
64
+
65
+ * `feed_each` yields each complete object as it's parsed
66
+ * Useful for processing multiple objects from a stream
67
+
68
+ == Examples
69
+
70
+ .Streaming unpacking from network
71
+ ====
72
+ [source,ruby]
73
+ ----
74
+ require 'socket'
75
+
76
+ # Simulate receiving data in chunks
77
+ unpacker = Messagepack::Unpacker.new
78
+
79
+ chunks = ["\x81\xA3", "foo", "\xA5", "world"]
80
+
81
+ chunks.each do |chunk|
82
+ unpacker.feed(chunk)
83
+ obj = unpacker.read
84
+ if obj
85
+ puts "Received: #{obj.inspect}"
86
+ else
87
+ puts "Waiting for more data..."
88
+ end
89
+ end
90
+
91
+ # Output:
92
+ # Waiting for more data...
93
+ # Waiting for more data...
94
+ # Waiting for more data...
95
+ # Received: {"foo"=>"world"}
96
+ ----
97
+ ====
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Symbol extension
3
+ parent: Core topics
4
+ nav_order: 5
5
+ ---
6
+
7
+ [[symbol-extension]]
8
+ = Symbol extension
9
+
10
+ == Purpose
11
+
12
+ The symbol extension (type 0) provides efficient serialization of Ruby symbols.
13
+
14
+ == References
15
+
16
+ * link:../references/extensions[Extension types reference] - Symbol extension documentation
17
+
18
+ == Concepts
19
+
20
+ === Registering symbol type
21
+
22
+ [source,ruby]
23
+ ----
24
+ factory.register_type(0, Symbol)
25
+ ----
26
+
27
+ Where,
28
+
29
+ * `0` is the type ID for symbols
30
+ * The extension uses `to_sym` and `to_s` for packing/unpacking
31
+
32
+ === Symbol serialization
33
+
34
+ [source,ruby]
35
+ ----
36
+ factory.register_type(0, Symbol)
37
+ binary = factory.pack(:hello_symbol)
38
+ result = factory.unpack(binary) # => :hello_symbol
39
+ ----
40
+
41
+ Where,
42
+
43
+ * Symbols are serialized as their string representation
44
+ * Deserialization converts the string back to a symbol
45
+ * This is more efficient than serializing as strings
46
+
47
+ == Examples
48
+
49
+ .Symbol serialization in data structures
50
+ ====
51
+ [source,ruby]
52
+ ----
53
+ factory = Messagepack::Factory.new
54
+ factory.register_type(0, Symbol)
55
+
56
+ data = {
57
+ status: :active,
58
+ priority: :high,
59
+ tags: [:important, :urgent]
60
+ }
61
+
62
+ binary = factory.pack(data)
63
+ result = factory.unpack(binary)
64
+ # => {:status=>:active, :priority=>:high, :tags=>[:important, :urgent]}
65
+ ----
66
+ ====
67
+
68
+ NOTE: The symbol extension is not registered by default. You must register it
69
+ explicitly if you want to preserve symbols when serializing.
@@ -0,0 +1,88 @@
1
+ ---
2
+ title: Timestamp extension
3
+ parent: Core topics
4
+ nav_order: 4
5
+ ---
6
+
7
+ [[timestamp-extension]]
8
+ = Timestamp extension
9
+
10
+ == Purpose
11
+
12
+ The timestamp extension (type -1) provides nanosecond precision time handling
13
+ for Time objects.
14
+
15
+ == References
16
+
17
+ * link:../references/extensions[Extension types reference] - Timestamp specification
18
+ * link:../references/format[Format specification] - MessagePack timestamp format
19
+
20
+ == Concepts
21
+
22
+ === Timestamp formats
23
+
24
+ .MessagePack automatically selects the appropriate format
25
+ ====
26
+ [source]
27
+ ----
28
+ Timestamp32 - 4 bytes (seconds only, 32-bit)
29
+ Used when: nanoseconds == 0 and
30
+ seconds fit in 32 bits
31
+
32
+ Timestamp64 - 8 bytes (seconds + nanoseconds)
33
+ Used when: nanoseconds != 0 and
34
+ timestamp fits in 64 bits
35
+
36
+ Timestamp96 - 12 bytes (seconds + nanoseconds, 96-bit)
37
+ Used when: timestamp requires 96 bits
38
+ ----
39
+ ====
40
+
41
+ === Using timestamp with Time
42
+
43
+ [source,ruby]
44
+ ----
45
+ factory.register_type(-1, Time,
46
+ packer: Messagepack::Time::Packer,
47
+ unpacker: Messagepack::Time::Unpacker
48
+ )
49
+ ----
50
+
51
+ Where,
52
+
53
+ * `-1` is the reserved type ID for timestamps
54
+ * `Messagepack::Time::Packer` handles serialization with nanosecond precision
55
+ * `Messagepack::Time::Unpacker` handles deserialization
56
+
57
+ == Examples
58
+
59
+ .Timestamp serialization examples
60
+ ====
61
+ [source,ruby]
62
+ ----
63
+ factory = Messagepack::Factory.new
64
+ factory.register_type(-1, Time,
65
+ packer: Messagepack::Time::Packer,
66
+ unpacker: Messagepack::Time::Unpacker
67
+ )
68
+
69
+ # Current time with nanosecond precision
70
+ now = Time.now
71
+ binary = factory.pack(now)
72
+ restored = factory.unpack(binary)
73
+ puts restored.tv_nsec # Nanoseconds preserved
74
+
75
+ # Historical date
76
+ time = Time.utc(2020, 1, 1, 12, 30, 45)
77
+ binary = factory.pack(time)
78
+ puts binary.size # => 6 (fixext4 format)
79
+
80
+ # Future date with nanoseconds
81
+ future = Time.utc(2100, 6, 15, 0, 0, 0, 123456789)
82
+ binary = factory.pack(future)
83
+ puts binary.size # => 15 (ext8 with timestamp96)
84
+ ----
85
+ ====
86
+
87
+ NOTE: The default factory automatically includes the timestamp extension for Time
88
+ objects. You don't need to register it unless you create a custom factory.
@@ -0,0 +1,360 @@
1
+ ---
2
+ title: API reference
3
+ nav_order: 1
4
+ ---
5
+
6
+ == Purpose
7
+
8
+ Complete API reference for the MessagePack Ruby library.
9
+
10
+ == Module: Messagepack
11
+
12
+ === pack(value, io = nil)
13
+
14
+ Serialize a Ruby object to MessagePack binary format.
15
+
16
+ [source,ruby]
17
+ ----
18
+ Messagepack.pack(value) => binary_string
19
+ Messagepack.pack(value, io) => nil
20
+ ----
21
+
22
+ * `value` - The Ruby object to serialize
23
+ * `io` - Optional IO object to write to
24
+ * Returns - Binary string if no IO, nil if writing to IO
25
+
26
+ [source,ruby]
27
+ ----
28
+ binary = Messagepack.pack({hello: "world"})
29
+ File.open("data.msgpack", "wb") { |f| Messagepack.pack(data, f) }
30
+ ----
31
+
32
+ === unpack(data, options = {})
33
+
34
+ Deserialize MessagePack binary to a Ruby object.
35
+
36
+ [source,ruby]
37
+ ----
38
+ Messagepack.unpack(data) => object
39
+ Messagepack.unpack(io) => object
40
+ ----
41
+
42
+ * `data` - Binary string or IO object
43
+ * `options` - Hash of options:
44
+ ** `:symbolize_keys` - Convert hash keys to symbols (default: false)
45
+ * Returns - The deserialized Ruby object
46
+
47
+ [source,ruby]
48
+ ----
49
+ obj = Messagepack.unpack("\x81\xA5hello\xA5world")
50
+ obj = Messagepack.unpack(file)
51
+ obj = Messagepack.unpack(data, symbolize_keys: true)
52
+ ----
53
+
54
+ === load(data, options = {})
55
+
56
+ Alias for `unpack`.
57
+
58
+ === dump(value, io = nil)
59
+
60
+ Alias for `pack`.
61
+
62
+ == Class: Messagepack::Factory
63
+
64
+ === new()
65
+
66
+ Create a new factory instance.
67
+
68
+ [source,ruby]
69
+ ----
70
+ factory = Messagepack::Factory.new
71
+ ----
72
+
73
+ === register_type(type_id, class, packer:, unpacker:, recursive: false)
74
+
75
+ Register a custom extension type.
76
+
77
+ [source,ruby]
78
+ ----
79
+ factory.register_type(0x01, MyClass,
80
+ packer: :to_msgpack_ext,
81
+ unpacker: :from_msgpack_ext
82
+ )
83
+
84
+ factory.register_type(0x02, MyContainer,
85
+ packer: ->(obj, pk) { pk.write(obj.to_h) },
86
+ unpacker: ->(up) { MyContainer.from_hash(up.read) },
87
+ recursive: true
88
+ )
89
+ ----
90
+
91
+ * `type_id` - Integer from -128 to 127
92
+ * `class` - Ruby class to register
93
+ * `packer` - Symbol, method, or proc for serialization
94
+ * `unpacker` - Symbol, method, or proc for deserialization
95
+ * `recursive` - Enable recursive packing (default: false)
96
+
97
+ === packer()
98
+
99
+ Create a new packer instance.
100
+
101
+ [source,ruby]
102
+ ----
103
+ packer = factory.packer
104
+ ----
105
+
106
+ === unpacker()
107
+
108
+ Create a new unpacker instance.
109
+
110
+ [source,ruby]
111
+ ----
112
+ unpacker = factory.unpacker
113
+ ----
114
+
115
+ === pack(value, io = nil)
116
+
117
+ Pack a value using this factory.
118
+
119
+ [source,ruby]
120
+ ----
121
+ binary = factory.pack(value)
122
+ factory.pack(value, io)
123
+ ----
124
+
125
+ === unpack(data, options = {})
126
+
127
+ Unpack data using this factory.
128
+
129
+ [source,ruby]
130
+ ----
131
+ obj = factory.unpack(data)
132
+ ----
133
+
134
+ === pool(size = 4)
135
+
136
+ Create a thread-safe pool.
137
+
138
+ [source,ruby]
139
+ ----
140
+ pool = factory.pool(10)
141
+ data = pool.pack(object)
142
+ obj = pool.unpack(binary)
143
+ ----
144
+
145
+ === freeze()
146
+
147
+ Freeze the factory for thread safety.
148
+
149
+ [source,ruby]
150
+ ----
151
+ factory.freeze
152
+ ----
153
+
154
+ == Class: Messagepack::Packer
155
+
156
+ === write(value)
157
+
158
+ Write a value to the buffer.
159
+
160
+ [source,ruby]
161
+ ----
162
+ packer.write(nil)
163
+ packer.write(42)
164
+ packer.write("hello")
165
+ packer.write([1, 2, 3])
166
+ packer.write({a: 1})
167
+ ----
168
+
169
+ === to_s
170
+
171
+ Get the packed binary data.
172
+
173
+ [source,ruby]
174
+ ----
175
+ binary = packer.to_s
176
+ ----
177
+
178
+ === full_pack
179
+
180
+ Alias for `to_s`.
181
+
182
+ === write_array_header(size)
183
+
184
+ Write an array header for streaming.
185
+
186
+ [source,ruby]
187
+ ----
188
+ packer.write_array_header(3)
189
+ packer.write(1)
190
+ packer.write(2)
191
+ packer.write(3)
192
+ ----
193
+
194
+ === write_map_header(size)
195
+
196
+ Write a map header for streaming.
197
+
198
+ [source,ruby]
199
+ ----
200
+ packer.write_map_header(2)
201
+ packer.write("key1")
202
+ packer.write("value1")
203
+ packer.write("key2")
204
+ packer.write("value2")
205
+ ----
206
+
207
+ == Class: Messagepack::Unpacker
208
+
209
+ === new(io = nil, options = {})
210
+
211
+ Create a new unpacker.
212
+
213
+ [source,ruby]
214
+ ----
215
+ unpacker = Messagepack::Unpacker.new
216
+ unpacker = Messagepack::Unpacker.new(io)
217
+ unpacker = Messagepack::Unpacker.new(symbolize_keys: true)
218
+ ----
219
+
220
+ === feed(data)
221
+
222
+ Feed data to the buffer.
223
+
224
+ [source,ruby]
225
+ ----
226
+ unpacker.feed(partial_data)
227
+ unpacker.feed(more_data)
228
+ ----
229
+
230
+ === read
231
+
232
+ Read one complete object.
233
+
234
+ [source,ruby]
235
+ ----
236
+ obj = unpacker.read
237
+ # Returns nil if more data needed
238
+ ----
239
+
240
+ === read_map_header
241
+
242
+ Read map header and return size.
243
+
244
+ [source,ruby]
245
+ ----
246
+ size = unpacker.read_map_header
247
+ ----
248
+
249
+ === read_array_header
250
+
251
+ Read array header and return size.
252
+
253
+ [source,ruby]
254
+ ----
255
+ size = unpacker.read_array_header
256
+ ----
257
+
258
+ === skip
259
+
260
+ Skip one complete object.
261
+
262
+ [source,ruby]
263
+ ----
264
+ unpacker.skip
265
+ ----
266
+
267
+ === feed_each {|obj| ...}
268
+
269
+ Iterate over objects in the buffer.
270
+
271
+ [source,ruby]
272
+ ----
273
+ unpacker.feed_each(data) do |obj|
274
+ process(obj)
275
+ end
276
+ ----
277
+
278
+ === full_unpack
279
+
280
+ Read one object and reset.
281
+
282
+ [source,ruby]
283
+ ----
284
+ obj = unpacker.full_unpack(data)
285
+ ----
286
+
287
+ == Class: Messagepack::BinaryBuffer
288
+
289
+ === new(io = nil)
290
+
291
+ Create a new buffer.
292
+
293
+ [source,ruby]
294
+ ----
295
+ buffer = Messagepack::BinaryBuffer.new
296
+ buffer = Messagepack::BinaryBuffer.new(io)
297
+ ----
298
+
299
+ === <<(data)
300
+
301
+ Append data to buffer.
302
+
303
+ [source,ruby]
304
+ ----
305
+ buffer << "data"
306
+ ----
307
+
308
+ === read(n)
309
+
310
+ Read n bytes from buffer.
311
+
312
+ [source,ruby]
313
+ ----
314
+ data = buffer.read(4)
315
+ ----
316
+
317
+ === to_s
318
+
319
+ Get remaining data.
320
+
321
+ [source,ruby]
322
+ ----
323
+ data = buffer.to_s
324
+ ----
325
+
326
+ === skip
327
+
328
+ Skip one object.
329
+
330
+ [source,ruby]
331
+ ----
332
+ buffer.skip
333
+ ----
334
+
335
+ == Exceptions
336
+
337
+ === Messagepack::UnpackError
338
+
339
+ Base class for unpacking errors.
340
+
341
+ === Messagepack::MalformedFormatError
342
+
343
+ Raised when data is not valid MessagePack format.
344
+
345
+ [source,ruby]
346
+ ----
347
+ begin
348
+ Messagepack.unpack("invalid")
349
+ rescue Messagepack::MalformedFormatError => e
350
+ puts e.message
351
+ end
352
+ ----
353
+
354
+ === Messagepack::StackError
355
+
356
+ Raised when data is too deeply nested.
357
+
358
+ === Messagepack::TypeError
359
+
360
+ Raised when a type cannot be serialized.