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,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting started
|
|
3
|
+
nav_order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Purpose
|
|
7
|
+
|
|
8
|
+
This tutorial introduces the basics of using MessagePack Ruby to serialize and
|
|
9
|
+
deserialize Ruby objects.
|
|
10
|
+
|
|
11
|
+
== References
|
|
12
|
+
|
|
13
|
+
* link:../pages/serialization[Serialization] - Core serialization concepts
|
|
14
|
+
* link:../references/api[API reference] - Complete API documentation
|
|
15
|
+
|
|
16
|
+
== Prerequisites
|
|
17
|
+
|
|
18
|
+
* Basic knowledge of Ruby
|
|
19
|
+
* Ruby 2.7 or higher
|
|
20
|
+
|
|
21
|
+
== Installing the gem
|
|
22
|
+
|
|
23
|
+
Add to your Gemfile:
|
|
24
|
+
|
|
25
|
+
[source,ruby]
|
|
26
|
+
----
|
|
27
|
+
gem 'messagepack'
|
|
28
|
+
----
|
|
29
|
+
|
|
30
|
+
Or install directly:
|
|
31
|
+
|
|
32
|
+
[source,shell]
|
|
33
|
+
----
|
|
34
|
+
gem install messagepack
|
|
35
|
+
----
|
|
36
|
+
|
|
37
|
+
== Your first serialization
|
|
38
|
+
|
|
39
|
+
=== Basic pack and unpack
|
|
40
|
+
|
|
41
|
+
[source,ruby]
|
|
42
|
+
----
|
|
43
|
+
require 'messagepack'
|
|
44
|
+
|
|
45
|
+
# Pack some data
|
|
46
|
+
data = { message: "Hello, MessagePack!" }
|
|
47
|
+
binary = Messagepack.pack(data)
|
|
48
|
+
|
|
49
|
+
# Inspect the binary (non-printable characters escaped)
|
|
50
|
+
puts binary.inspect
|
|
51
|
+
# => "\\x81\\xA7message\\xB4Hello, MessagePack!"
|
|
52
|
+
|
|
53
|
+
# Unpack the data
|
|
54
|
+
result = Messagepack.unpack(binary)
|
|
55
|
+
puts result.inspect
|
|
56
|
+
# => {"message"=>"Hello, MessagePack!"}
|
|
57
|
+
----
|
|
58
|
+
|
|
59
|
+
=== Working with different types
|
|
60
|
+
|
|
61
|
+
[source,ruby]
|
|
62
|
+
----
|
|
63
|
+
# Numbers
|
|
64
|
+
Messagepack.pack(42) # Integer
|
|
65
|
+
Messagepack.pack(3.14) # Float
|
|
66
|
+
Messagepack.pack(-100) # Negative integer
|
|
67
|
+
|
|
68
|
+
# Strings and symbols
|
|
69
|
+
Messagepack.pack("hello") # String
|
|
70
|
+
Messagepack.pack(:world) # Symbol (serialized as string)
|
|
71
|
+
|
|
72
|
+
# Collections
|
|
73
|
+
Messagepack.pack([1, 2, 3]) # Array
|
|
74
|
+
Messagepack.pack({a: 1}) # Hash
|
|
75
|
+
|
|
76
|
+
# Special values
|
|
77
|
+
Messagepack.pack(nil) # Nil
|
|
78
|
+
Messagepack.pack(true) # Boolean true
|
|
79
|
+
Messagepack.pack(false) # Boolean false
|
|
80
|
+
----
|
|
81
|
+
|
|
82
|
+
== Working with IO
|
|
83
|
+
|
|
84
|
+
=== Writing to a file
|
|
85
|
+
|
|
86
|
+
[source,ruby]
|
|
87
|
+
----
|
|
88
|
+
data = { name: "Alice", age: 30, skills: ["Ruby", "Python"] }
|
|
89
|
+
|
|
90
|
+
# Write to file
|
|
91
|
+
File.open("data.msgpack", "wb") do |file|
|
|
92
|
+
file.write(Messagepack.pack(data))
|
|
93
|
+
end
|
|
94
|
+
----
|
|
95
|
+
|
|
96
|
+
=== Reading from a file
|
|
97
|
+
|
|
98
|
+
[source,ruby]
|
|
99
|
+
----
|
|
100
|
+
# Read from file
|
|
101
|
+
data = File.read("data.msgpack", encoding: Encoding::BINARY)
|
|
102
|
+
result = Messagepack.unpack(data)
|
|
103
|
+
# => {"name"=>"Alice", "age"=>30, "skills"=>["Ruby", "Python"]}
|
|
104
|
+
----
|
|
105
|
+
|
|
106
|
+
=== Using IO directly
|
|
107
|
+
|
|
108
|
+
[source,ruby]
|
|
109
|
+
----
|
|
110
|
+
# Unpack can read from IO directly
|
|
111
|
+
File.open("data.msgpack", "rb") do |file|
|
|
112
|
+
result = Messagepack.unpack(file)
|
|
113
|
+
end
|
|
114
|
+
----
|
|
115
|
+
|
|
116
|
+
== Practical example: Configuration file
|
|
117
|
+
|
|
118
|
+
Create a simple configuration system using MessagePack:
|
|
119
|
+
|
|
120
|
+
[source,ruby]
|
|
121
|
+
----
|
|
122
|
+
require 'messagepack'
|
|
123
|
+
|
|
124
|
+
class Config
|
|
125
|
+
def initialize(path)
|
|
126
|
+
@path = path
|
|
127
|
+
@data = load
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def [](key)
|
|
131
|
+
@data[key]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def []=(key, value)
|
|
135
|
+
@data[key] = value
|
|
136
|
+
save
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def load
|
|
142
|
+
if File.exist?(@path)
|
|
143
|
+
Messagepack.unpack(File.read(@path, encoding: Encoding::BINARY))
|
|
144
|
+
else
|
|
145
|
+
{}
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def save
|
|
150
|
+
File.write(@path, Messagepack.pack(@data))
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Usage
|
|
155
|
+
config = Config.new("config.msgpack")
|
|
156
|
+
config[:database] = { host: "localhost", port: 5432 }
|
|
157
|
+
config[:debug] = true
|
|
158
|
+
|
|
159
|
+
puts config[:database][:host] # => "localhost"
|
|
160
|
+
----
|
|
161
|
+
|
|
162
|
+
== Next steps
|
|
163
|
+
|
|
164
|
+
* link:../tutorials/extension-types[Custom extension types] - Learn to serialize custom classes
|
|
165
|
+
* link:../guides/performance[Performance guide] - Optimize your serialization code
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tutorials
|
|
3
|
+
nav_order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Purpose
|
|
7
|
+
|
|
8
|
+
Tutorials provide step-by-step learning paths for mastering MessagePack Ruby.
|
|
9
|
+
|
|
10
|
+
== Tutorials
|
|
11
|
+
|
|
12
|
+
* link:../tutorials/getting-started[Getting started] - Introduction to basic serialization
|
|
13
|
+
* link:../tutorials/extension-types[Custom extension types] - Creating custom type mappings
|
|
14
|
+
* link:../tutorials/thread-safety[Thread-safe usage] - Using factories in multi-threaded applications
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Thread-safe usage
|
|
3
|
+
nav_order: 3
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Purpose
|
|
7
|
+
|
|
8
|
+
This tutorial explains how to use MessagePack factories safely in multi-threaded
|
|
9
|
+
applications.
|
|
10
|
+
|
|
11
|
+
== References
|
|
12
|
+
|
|
13
|
+
* link:../pages/factory-pattern[Factory pattern] - Factory concepts
|
|
14
|
+
* link:../guides/performance[Performance guide] - Performance optimization
|
|
15
|
+
|
|
16
|
+
== Prerequisites
|
|
17
|
+
|
|
18
|
+
* Completed link:../tutorials/getting-started[Getting started] tutorial
|
|
19
|
+
* Basic understanding of Ruby threads
|
|
20
|
+
|
|
21
|
+
== Understanding thread safety
|
|
22
|
+
|
|
23
|
+
The `Messagepack::Factory` class provides two approaches for thread-safe usage:
|
|
24
|
+
|
|
25
|
+
* **Frozen factory** - Safe for concurrent reads
|
|
26
|
+
* **Factory pool** - Safe for concurrent reads and writes
|
|
27
|
+
|
|
28
|
+
== Using frozen factories
|
|
29
|
+
|
|
30
|
+
For concurrent serialization with custom types:
|
|
31
|
+
|
|
32
|
+
[source,ruby]
|
|
33
|
+
----
|
|
34
|
+
# Create and configure factory
|
|
35
|
+
factory = Messagepack::Factory.new
|
|
36
|
+
factory.register_type(0x01, MyClass, packer: ..., unpacker: ...)
|
|
37
|
+
|
|
38
|
+
# Freeze to make thread-safe
|
|
39
|
+
factory.freeze
|
|
40
|
+
|
|
41
|
+
# Now safe to use from multiple threads
|
|
42
|
+
threads = 10.times.map do
|
|
43
|
+
Thread.new { factory.pack(my_object) }
|
|
44
|
+
end
|
|
45
|
+
results = threads.map(&:value)
|
|
46
|
+
----
|
|
47
|
+
|
|
48
|
+
NOTE: Frozen factories are safe for concurrent packing but not for unpacking
|
|
49
|
+
from a shared unpacker. Use pools for full thread safety.
|
|
50
|
+
|
|
51
|
+
== Using factory pools
|
|
52
|
+
|
|
53
|
+
Pools provide the highest level of thread safety:
|
|
54
|
+
|
|
55
|
+
[source,ruby]
|
|
56
|
+
----
|
|
57
|
+
# Create pool with 5 instances
|
|
58
|
+
pool = factory.pool(5)
|
|
59
|
+
|
|
60
|
+
# Use from multiple threads
|
|
61
|
+
threads = 20.times.map do |i|
|
|
62
|
+
Thread.new do
|
|
63
|
+
# Each thread gets its own packer/unpacker from the pool
|
|
64
|
+
data = { id: i, value: "thread-#{i}" }
|
|
65
|
+
binary = pool.pack(data)
|
|
66
|
+
pool.unpack(binary)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
results = threads.map(&:value)
|
|
71
|
+
----
|
|
72
|
+
|
|
73
|
+
=== Pool size guidelines
|
|
74
|
+
|
|
75
|
+
Choose pool size based on your concurrency needs:
|
|
76
|
+
|
|
77
|
+
[source,ruby]
|
|
78
|
+
----
|
|
79
|
+
# For thread pool of 10 workers
|
|
80
|
+
pool = factory.pool(10)
|
|
81
|
+
|
|
82
|
+
# For unlimited concurrency, use number of CPU cores
|
|
83
|
+
pool = factory.pool(Erl::Concurrency.available_processors)
|
|
84
|
+
|
|
85
|
+
# Default: 4 instances
|
|
86
|
+
pool = factory.pool(4)
|
|
87
|
+
----
|
|
88
|
+
|
|
89
|
+
== Practical example: Web server
|
|
90
|
+
|
|
91
|
+
[source,ruby]
|
|
92
|
+
----
|
|
93
|
+
require 'messagepack'
|
|
94
|
+
|
|
95
|
+
class Cache
|
|
96
|
+
def initialize
|
|
97
|
+
@factory = Messagepack::Factory.new
|
|
98
|
+
@factory.register_type(0x01, CachedObject, ...)
|
|
99
|
+
@pool = @factory.pool(10)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def put(key, value)
|
|
103
|
+
binary = @pool.pack(value)
|
|
104
|
+
redis.set("cache:#{key}", binary)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def get(key)
|
|
108
|
+
data = redis.get("cache:#{key}")
|
|
109
|
+
return nil unless data
|
|
110
|
+
@pool.unpack(data)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Thread-safe for web server
|
|
115
|
+
cache = Cache.new
|
|
116
|
+
|
|
117
|
+
# In web requests (concurrent)
|
|
118
|
+
Thread.new do
|
|
119
|
+
cache.put("user:123", user_data)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
Thread.new do
|
|
123
|
+
user = cache.get("user:123")
|
|
124
|
+
end
|
|
125
|
+
----
|
|
126
|
+
|
|
127
|
+
== Performance considerations
|
|
128
|
+
|
|
129
|
+
[source,ruby]
|
|
130
|
+
----
|
|
131
|
+
# Benchmark different approaches
|
|
132
|
+
require 'benchmark'
|
|
133
|
+
|
|
134
|
+
n = 10000
|
|
135
|
+
|
|
136
|
+
Benchmark.bm do |x|
|
|
137
|
+
x.report("pack:") do
|
|
138
|
+
factory = Messagepack::Factory.new
|
|
139
|
+
n.times { factory.pack(data) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
x.report("pool(2):") do
|
|
143
|
+
pool = factory.pool(2)
|
|
144
|
+
n.times { pool.pack(data) }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
x.report("pool(10):") do
|
|
148
|
+
pool = factory.pool(10)
|
|
149
|
+
n.times { pool.pack(data) }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
----
|
|
153
|
+
|
|
154
|
+
== Next steps
|
|
155
|
+
|
|
156
|
+
* link:../guides/performance[Performance guide] - Optimize for performance
|
|
157
|
+
* link:../references/api[API reference] - Complete API documentation
|
data/docs/index.adoc
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: MessagePack Ruby
|
|
3
|
+
nav_order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Purpose
|
|
7
|
+
|
|
8
|
+
MessagePack Ruby is a pure Ruby implementation of the https://msgpack.org/[MessagePack] binary serialization format. MessagePack is an efficient binary serialization format that enables exchange of data among multiple languages like JSON, but is faster and smaller.
|
|
9
|
+
|
|
10
|
+
This implementation provides:
|
|
11
|
+
|
|
12
|
+
* Pure Ruby implementation (no C extension required)
|
|
13
|
+
* Full compatibility with the MessagePack specification
|
|
14
|
+
* Support for custom extension types
|
|
15
|
+
* Thread-safe factory pattern for packer/unpacker reuse
|
|
16
|
+
* Streaming unpacker for incremental parsing
|
|
17
|
+
* Comprehensive timestamp support with nanosecond precision
|
|
18
|
+
|
|
19
|
+
== Getting started
|
|
20
|
+
|
|
21
|
+
=== Installation
|
|
22
|
+
|
|
23
|
+
Add this line to your application's Gemfile:
|
|
24
|
+
|
|
25
|
+
[source,ruby]
|
|
26
|
+
----
|
|
27
|
+
gem 'messagepack'
|
|
28
|
+
----
|
|
29
|
+
|
|
30
|
+
And then execute:
|
|
31
|
+
|
|
32
|
+
[source,shell]
|
|
33
|
+
----
|
|
34
|
+
bundle install
|
|
35
|
+
----
|
|
36
|
+
|
|
37
|
+
Or install it yourself as:
|
|
38
|
+
|
|
39
|
+
[source,shell]
|
|
40
|
+
----
|
|
41
|
+
gem install messagepack
|
|
42
|
+
----
|
|
43
|
+
|
|
44
|
+
=== Quick start
|
|
45
|
+
|
|
46
|
+
[source,ruby]
|
|
47
|
+
----
|
|
48
|
+
require 'messagepack'
|
|
49
|
+
|
|
50
|
+
# Serialize data
|
|
51
|
+
data = { hello: "world", count: 42 }
|
|
52
|
+
binary = Messagepack.pack(data)
|
|
53
|
+
|
|
54
|
+
# Deserialize data
|
|
55
|
+
result = Messagepack.unpack(binary)
|
|
56
|
+
# => {"hello"=>"world", "count"=>42}
|
|
57
|
+
----
|
|
58
|
+
|
|
59
|
+
== Core concepts
|
|
60
|
+
|
|
61
|
+
* link:pages/serialization[Serialization] - Converting Ruby objects to binary format and back
|
|
62
|
+
* link:pages/factory-pattern[Factory pattern] - Thread-safe packer/unpacker management
|
|
63
|
+
* link:pages/extension-types[Extension types] - Custom type registration system
|
|
64
|
+
* link:pages/buffer[Buffer management] - Efficient chunked binary data storage
|
|
65
|
+
* link:pages/streaming[Streaming] - Incremental data parsing
|
|
66
|
+
|
|
67
|
+
== Learn more
|
|
68
|
+
|
|
69
|
+
* link:tutorials/getting-started[Getting started tutorial] - Step-by-step introduction
|
|
70
|
+
* link:guides/performance[Performance guide] - Optimization techniques and best practices
|
|
71
|
+
* link:references/api[API reference] - Complete API documentation
|
|
72
|
+
|
|
73
|
+
== External resources
|
|
74
|
+
|
|
75
|
+
* https://msgpack.org/[MessagePack specification] - Official format specification
|
|
76
|
+
* https://github.com/msgpack/msgpack/blob/master/spec.md[MessagePack spec] - Detailed format documentation
|
|
77
|
+
* https://github.com/lutaml/messagepack[GitHub repository] - Source code and issues
|
data/docs/lychee.toml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Lychee link checker configuration
|
|
2
|
+
|
|
3
|
+
# Exclude specific URLs
|
|
4
|
+
exclude = [
|
|
5
|
+
# Exclude localhost links
|
|
6
|
+
"http://localhost",
|
|
7
|
+
"https://localhost",
|
|
8
|
+
|
|
9
|
+
# Exclude example domains that may not resolve
|
|
10
|
+
"http://example.com",
|
|
11
|
+
"https://example.com",
|
|
12
|
+
|
|
13
|
+
# Exclude links that require authentication
|
|
14
|
+
"https://github.com/lutaml/*",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# Only check http and https links
|
|
18
|
+
schemes = ["http", "https"]
|
|
19
|
+
|
|
20
|
+
# Maximum number of concurrent requests
|
|
21
|
+
max_concurrency = 8
|
|
22
|
+
|
|
23
|
+
# Maximum number of redirects to follow
|
|
24
|
+
max_redirects = 10
|
|
25
|
+
|
|
26
|
+
# Request timeout in seconds
|
|
27
|
+
timeout = 20
|
|
28
|
+
|
|
29
|
+
# User agent for requests
|
|
30
|
+
user_agent = "lychee/0.14.0"
|
|
31
|
+
|
|
32
|
+
# Accept 200, 204, 301, 302, 304, 307, 308 status codes
|
|
33
|
+
accept = [200, 204, 301, 302, 304, 307, 308]
|
|
34
|
+
|
|
35
|
+
# Method to use for requests (GET or HEAD)
|
|
36
|
+
method = "get"
|
|
37
|
+
|
|
38
|
+
# Verbose output
|
|
39
|
+
verbose = false
|
|
40
|
+
|
|
41
|
+
# Do not show progress
|
|
42
|
+
no_progress = true
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'extensions/value'
|
|
4
|
+
|
|
5
|
+
module Messagepack
|
|
6
|
+
# Bigint extension type for arbitrary precision integers.
|
|
7
|
+
#
|
|
8
|
+
# This class handles integers that are too large for 64-bit representation.
|
|
9
|
+
# Uses the same format as msgpack-ruby: 32-bit big-endian chunks.
|
|
10
|
+
#
|
|
11
|
+
class Bigint
|
|
12
|
+
# Bigint extension type ID
|
|
13
|
+
TYPE = 0
|
|
14
|
+
|
|
15
|
+
# Number of bits per chunk
|
|
16
|
+
CHUNK_BITLENGTH = 32
|
|
17
|
+
|
|
18
|
+
# Ruby pack format: sign byte + 32-bit big-endian chunks
|
|
19
|
+
FORMAT = 'CL>*'
|
|
20
|
+
|
|
21
|
+
attr_reader :data
|
|
22
|
+
|
|
23
|
+
def initialize(data)
|
|
24
|
+
@data = data
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Serialize an integer to MessagePack bigint extension format.
|
|
28
|
+
#
|
|
29
|
+
# @param int [Integer] The integer to serialize
|
|
30
|
+
# @return [String] Binary payload
|
|
31
|
+
#
|
|
32
|
+
def self.to_msgpack_ext(int)
|
|
33
|
+
# Format: [sign(1 byte)][32-bit big-endian chunks...]
|
|
34
|
+
# sign: 0 for positive, 1 for negative
|
|
35
|
+
# chunks: absolute value split into 32-bit pieces, LSB first, each in big-endian byte order
|
|
36
|
+
|
|
37
|
+
if int == 0
|
|
38
|
+
return "\x00".b
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
members = []
|
|
42
|
+
|
|
43
|
+
# Sign byte
|
|
44
|
+
if int < 0
|
|
45
|
+
int = -int
|
|
46
|
+
members << 1
|
|
47
|
+
else
|
|
48
|
+
members << 0
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Split into 32-bit chunks (least significant chunk first)
|
|
52
|
+
base = (2 ** CHUNK_BITLENGTH) - 1
|
|
53
|
+
while int > 0
|
|
54
|
+
members << (int & base)
|
|
55
|
+
int >>= CHUNK_BITLENGTH
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Pack as sign byte + 32-bit big-endian chunks
|
|
59
|
+
members.pack(FORMAT)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Deserialize from binary payload.
|
|
63
|
+
#
|
|
64
|
+
# @param data [String] Binary payload
|
|
65
|
+
# @return [Integer] The deserialized integer
|
|
66
|
+
#
|
|
67
|
+
def self.from_msgpack_ext(data)
|
|
68
|
+
return 0 if data.nil? || data.empty?
|
|
69
|
+
|
|
70
|
+
# Unpack as sign byte + 32-bit big-endian chunks
|
|
71
|
+
parts = data.unpack(FORMAT)
|
|
72
|
+
|
|
73
|
+
return 0 if parts.empty?
|
|
74
|
+
|
|
75
|
+
sign = parts.shift
|
|
76
|
+
|
|
77
|
+
return 0 if parts.empty?
|
|
78
|
+
|
|
79
|
+
# Reconstruct integer from chunks (LSB first)
|
|
80
|
+
sum = parts.pop.to_i
|
|
81
|
+
parts.reverse_each do |part|
|
|
82
|
+
sum = (sum << CHUNK_BITLENGTH) | part.to_i
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
sign == 0 ? sum : -sum
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Create a Bigint from an integer.
|
|
89
|
+
#
|
|
90
|
+
# @param int [Integer] The integer
|
|
91
|
+
# @return [Bigint] A new Bigint instance
|
|
92
|
+
#
|
|
93
|
+
def self.from_int(int)
|
|
94
|
+
new(to_msgpack_ext(int))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Convert to integer.
|
|
98
|
+
#
|
|
99
|
+
# @return [Integer] The integer value
|
|
100
|
+
#
|
|
101
|
+
def to_int
|
|
102
|
+
self.class.from_msgpack_ext(@data)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Equality comparison.
|
|
106
|
+
#
|
|
107
|
+
# @param other [Object] Another object
|
|
108
|
+
# @return [Boolean]
|
|
109
|
+
#
|
|
110
|
+
def ==(other)
|
|
111
|
+
return false unless other.is_a?(Bigint)
|
|
112
|
+
to_int == other.to_int
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
alias eql? ==
|
|
116
|
+
|
|
117
|
+
def hash
|
|
118
|
+
to_int.hash
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# String representation.
|
|
122
|
+
#
|
|
123
|
+
# @return [String]
|
|
124
|
+
#
|
|
125
|
+
def to_s
|
|
126
|
+
"Bigint(#{@data.bytes.map { |b| '0x%02x' % b }.join(' ')})"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
alias inspect to_s
|
|
130
|
+
end
|
|
131
|
+
end
|