paquito 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile.lock +23 -23
- data/README.md +11 -1
- data/benchmark/string-bypass.rb +35 -0
- data/lib/paquito/single_byte_prefix_version.rb +12 -2
- data/lib/paquito/single_byte_prefix_version_with_string_bypass.rb +57 -0
- data/lib/paquito/types.rb +5 -5
- data/lib/paquito/version.rb +1 -1
- data/lib/paquito.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cfefa4171fc3e9f968368e99c134e864d493c6651ed21aff0e1bc299a95fb7f
|
4
|
+
data.tar.gz: e92cacde6d2aa0867b740f99a001d3ff109f4c88eddb7d89594305a579122af3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e032eb7438d07371862c7f9015b8c0a2f72dc8a93004387f1b151f75d06614dba89bc97da21d3eb83399f1c86909f1dc0bbd5702ec8cc4fef1f5001f512a3da
|
7
|
+
data.tar.gz: e1bd239ec600c28dc5e322b8a8a95daeb539aaada4c9167cf046f5ee5716eb3a6231487b663b534d0db8cb6e1ce2ce53d1dd1b6706184b57f36890edbcbfa663
|
data/.github/workflows/ci.yml
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
paquito (0.
|
4
|
+
paquito (0.8.0)
|
5
5
|
msgpack (>= 1.5.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (6.1.
|
11
|
-
activesupport (= 6.1.
|
12
|
-
activerecord (6.1.
|
13
|
-
activemodel (= 6.1.
|
14
|
-
activesupport (= 6.1.
|
15
|
-
activesupport (6.1.
|
10
|
+
activemodel (6.1.6.1)
|
11
|
+
activesupport (= 6.1.6.1)
|
12
|
+
activerecord (6.1.6.1)
|
13
|
+
activemodel (= 6.1.6.1)
|
14
|
+
activesupport (= 6.1.6.1)
|
15
|
+
activesupport (6.1.6.1)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (>= 1.6, < 2)
|
18
18
|
minitest (>= 5.1)
|
@@ -21,38 +21,38 @@ GEM
|
|
21
21
|
ast (2.4.2)
|
22
22
|
benchmark-ips (2.10.0)
|
23
23
|
byebug (11.1.3)
|
24
|
-
concurrent-ruby (1.1.
|
25
|
-
i18n (1.
|
24
|
+
concurrent-ruby (1.1.10)
|
25
|
+
i18n (1.12.0)
|
26
26
|
concurrent-ruby (~> 1.0)
|
27
|
-
minitest (5.
|
28
|
-
msgpack (1.
|
29
|
-
parallel (1.
|
30
|
-
parser (3.1.1
|
27
|
+
minitest (5.16.3)
|
28
|
+
msgpack (1.6.0)
|
29
|
+
parallel (1.22.1)
|
30
|
+
parser (3.1.2.1)
|
31
31
|
ast (~> 2.4.1)
|
32
32
|
rainbow (3.1.1)
|
33
33
|
rake (13.0.6)
|
34
|
-
regexp_parser (2.
|
34
|
+
regexp_parser (2.5.0)
|
35
35
|
rexml (3.2.5)
|
36
|
-
rubocop (1.
|
36
|
+
rubocop (1.30.1)
|
37
37
|
parallel (~> 1.10)
|
38
38
|
parser (>= 3.1.0.0)
|
39
39
|
rainbow (>= 2.2.2, < 4.0)
|
40
40
|
regexp_parser (>= 1.8, < 3.0)
|
41
|
-
rexml
|
42
|
-
rubocop-ast (>= 1.
|
41
|
+
rexml (>= 3.2.5, < 4.0)
|
42
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
43
43
|
ruby-progressbar (~> 1.7)
|
44
44
|
unicode-display_width (>= 1.4.0, < 3.0)
|
45
|
-
rubocop-ast (1.
|
45
|
+
rubocop-ast (1.21.0)
|
46
46
|
parser (>= 3.1.1.0)
|
47
47
|
rubocop-shopify (2.5.0)
|
48
48
|
rubocop (~> 1.25)
|
49
49
|
ruby-progressbar (1.11.0)
|
50
|
-
sorbet-runtime (0.5.
|
51
|
-
sqlite3 (1.4.
|
52
|
-
tzinfo (2.0.
|
50
|
+
sorbet-runtime (0.5.10365)
|
51
|
+
sqlite3 (1.4.4)
|
52
|
+
tzinfo (2.0.5)
|
53
53
|
concurrent-ruby (~> 1.0)
|
54
|
-
unicode-display_width (2.
|
55
|
-
zeitwerk (2.
|
54
|
+
unicode-display_width (2.2.0)
|
55
|
+
zeitwerk (2.6.0)
|
56
56
|
|
57
57
|
PLATFORMS
|
58
58
|
ruby
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Paquito
|
2
2
|
|
3
|
-
`Paquito`
|
3
|
+
`Paquito` provides utility classes to define optimized and evolutive serializers.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -62,6 +62,16 @@ coder.dump([1]) # => "\x01[1]"
|
|
62
62
|
coder.load("\x00---\n:foo: 42") # => { foo: 42 }
|
63
63
|
```
|
64
64
|
|
65
|
+
### `SingleBytePrefixVersionWithStringBypass`
|
66
|
+
|
67
|
+
Works like `Paquito::SingleBytePrefixVersion` except that versions `253`, `254` and `255` are reserved for serializing strings
|
68
|
+
in an optimized way.
|
69
|
+
|
70
|
+
When the object to serialize is an `UTF-8`, `ASCII` or `BINARY` string, rather than invoking the underlying serializer, it simply
|
71
|
+
prepends a single byte to the string which indicates the encoding.
|
72
|
+
|
73
|
+
The larger the string the larger the speed gain is, e.g. for a 1MB string, it's over 500x faster than going through `MessagePack` or `Marshal`.
|
74
|
+
|
65
75
|
### `CommentPrefixVersion`
|
66
76
|
|
67
77
|
Similar to the single byte prefix, but meant to be human readable and to allow for migrating unversioned payloads.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "paquito"
|
6
|
+
require "benchmark/ips"
|
7
|
+
|
8
|
+
CODEC = Paquito::CodecFactory.build([])
|
9
|
+
VERSIONED = Paquito::SingleBytePrefixVersion.new(0, 0 => CODEC)
|
10
|
+
BYPASS = Paquito::SingleBytePrefixVersionWithStringBypass.new(0, 0 => CODEC)
|
11
|
+
|
12
|
+
[100, 10_000, 1_000_000].each do |size|
|
13
|
+
string = Random.bytes(size).freeze
|
14
|
+
msgpack_payload = VERSIONED.dump(string).freeze
|
15
|
+
bypass_payload = BYPASS.dump(string).freeze
|
16
|
+
marshal_payload = Marshal.dump(string).freeze
|
17
|
+
|
18
|
+
puts " === Read #{size}B ==="
|
19
|
+
Benchmark.ips do |x|
|
20
|
+
x.report("marshal") { Marshal.load(marshal_payload) }
|
21
|
+
x.report("msgpack") { VERSIONED.load(msgpack_payload) }
|
22
|
+
x.report("bypass") { BYPASS.load(bypass_payload) }
|
23
|
+
x.compare!(order: :baseline)
|
24
|
+
end
|
25
|
+
|
26
|
+
puts " === Write #{size}B ==="
|
27
|
+
Benchmark.ips do |x|
|
28
|
+
x.report("marshal") { Marshal.dump(string) }
|
29
|
+
x.report("msgpack") { VERSIONED.dump(string) }
|
30
|
+
x.report("bypass") { BYPASS.dump(string) }
|
31
|
+
x.compare!(order: :baseline)
|
32
|
+
end
|
33
|
+
|
34
|
+
puts
|
35
|
+
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module Paquito
|
4
4
|
class SingleBytePrefixVersion
|
5
5
|
def initialize(current_version, coders)
|
6
|
-
@current_version = current_version
|
7
|
-
@coders = coders.transform_values { |c| Paquito.cast(c) }
|
6
|
+
@current_version = validate_version(current_version)
|
7
|
+
@coders = coders.transform_keys { |v| validate_version(v) }.transform_values { |c| Paquito.cast(c) }
|
8
8
|
@current_coder = coders.fetch(current_version)
|
9
9
|
end
|
10
10
|
|
@@ -23,5 +23,15 @@ module Paquito
|
|
23
23
|
end
|
24
24
|
coder.load(payload.byteslice(1..-1))
|
25
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def validate_version(version)
|
30
|
+
unless (0..255).cover?(version)
|
31
|
+
raise ArgumentError, "Invalid version #{version.inspect}, versions must be an integer between 0 and 255"
|
32
|
+
end
|
33
|
+
|
34
|
+
version
|
35
|
+
end
|
26
36
|
end
|
27
37
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Paquito
|
4
|
+
class SingleBytePrefixVersionWithStringBypass < SingleBytePrefixVersion
|
5
|
+
UTF8_VERSION = 255
|
6
|
+
BINARY_VERSION = 254
|
7
|
+
ASCII_VERSION = 253
|
8
|
+
|
9
|
+
def dump(object)
|
10
|
+
if object.class == String # We don't want to match subclasses
|
11
|
+
case object.encoding
|
12
|
+
when Encoding::UTF_8
|
13
|
+
UTF8_VERSION.chr(Encoding::BINARY) << object
|
14
|
+
when Encoding::BINARY
|
15
|
+
BINARY_VERSION.chr(Encoding::BINARY) << object
|
16
|
+
when Encoding::US_ASCII
|
17
|
+
ASCII_VERSION.chr(Encoding::BINARY) << object
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load(payload)
|
27
|
+
payload_version = payload.getbyte(0)
|
28
|
+
unless payload_version
|
29
|
+
raise UnsupportedCodec, "Missing version byte."
|
30
|
+
end
|
31
|
+
|
32
|
+
case payload_version
|
33
|
+
when UTF8_VERSION
|
34
|
+
payload.byteslice(1..-1).force_encoding(Encoding::UTF_8)
|
35
|
+
when BINARY_VERSION
|
36
|
+
payload.byteslice(1..-1).force_encoding(Encoding::BINARY)
|
37
|
+
when ASCII_VERSION
|
38
|
+
payload.byteslice(1..-1).force_encoding(Encoding::US_ASCII)
|
39
|
+
else
|
40
|
+
coder = @coders.fetch(payload_version) do
|
41
|
+
raise UnsupportedCodec, "Unsupported packer version #{payload_version}"
|
42
|
+
end
|
43
|
+
coder.load(payload.byteslice(1..-1))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def validate_version(version)
|
50
|
+
unless (0..252).cover?(version)
|
51
|
+
raise ArgumentError, "Invalid version #{version.inspect}, versions must be an integer between 0 and 252"
|
52
|
+
end
|
53
|
+
|
54
|
+
version
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/paquito/types.rb
CHANGED
@@ -160,7 +160,7 @@ module Paquito
|
|
160
160
|
|
161
161
|
packer.write(value.to_h)
|
162
162
|
end,
|
163
|
-
unpacker: ->(unpacker) { HashWithIndifferentAccess.new(unpacker.read) },
|
163
|
+
unpacker: ->(unpacker) { ActiveSupport::HashWithIndifferentAccess.new(unpacker.read) },
|
164
164
|
recursive: true,
|
165
165
|
},
|
166
166
|
"ActiveSupport::TimeWithZone" => {
|
@@ -206,11 +206,11 @@ module Paquito
|
|
206
206
|
class << self
|
207
207
|
def register(factory, types)
|
208
208
|
types.each do |type|
|
209
|
-
name = type.name
|
210
|
-
|
211
209
|
# Up to Rails 7 ActiveSupport::TimeWithZone#name returns "Time"
|
212
|
-
|
213
|
-
|
210
|
+
name = if defined?(ActiveSupport::TimeWithZone) && type == ActiveSupport::TimeWithZone
|
211
|
+
"ActiveSupport::TimeWithZone"
|
212
|
+
else
|
213
|
+
type.name
|
214
214
|
end
|
215
215
|
|
216
216
|
type_attributes = TYPES.fetch(name)
|
data/lib/paquito/version.rb
CHANGED
data/lib/paquito.rb
CHANGED
@@ -16,6 +16,7 @@ require "paquito/safe_yaml"
|
|
16
16
|
require "paquito/conditional_compressor"
|
17
17
|
require "paquito/cache_entry_coder"
|
18
18
|
require "paquito/single_byte_prefix_version"
|
19
|
+
require "paquito/single_byte_prefix_version_with_string_bypass"
|
19
20
|
require "paquito/comment_prefix_version"
|
20
21
|
require "paquito/types"
|
21
22
|
require "paquito/codec_factory"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paquito
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -41,6 +41,7 @@ files:
|
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
43
43
|
- benchmark/msgpack-pooling.rb
|
44
|
+
- benchmark/string-bypass.rb
|
44
45
|
- bin/console
|
45
46
|
- bin/setup
|
46
47
|
- dev.yml
|
@@ -57,6 +58,7 @@ files:
|
|
57
58
|
- lib/paquito/safe_yaml.rb
|
58
59
|
- lib/paquito/serialized_column.rb
|
59
60
|
- lib/paquito/single_byte_prefix_version.rb
|
61
|
+
- lib/paquito/single_byte_prefix_version_with_string_bypass.rb
|
60
62
|
- lib/paquito/struct.rb
|
61
63
|
- lib/paquito/translate_errors.rb
|
62
64
|
- lib/paquito/typed_struct.rb
|