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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae7b47980ce61a6b1c3d650217d29a5c196df0c69470a36b74f643d0ded5334c
4
- data.tar.gz: 06a85f164bcf5a0e472b4ebf41ce1a9680f71557663ed3176cb7e0df368e460b
3
+ metadata.gz: 2cfefa4171fc3e9f968368e99c134e864d493c6651ed21aff0e1bc299a95fb7f
4
+ data.tar.gz: e92cacde6d2aa0867b740f99a001d3ff109f4c88eddb7d89594305a579122af3
5
5
  SHA512:
6
- metadata.gz: 7fcc3454d84b6cfac7ffe5f3b33780fc23aa609cad375db0e35b43a28582fc3b5dbb6913fc1b3e991b1f034418abe9dbd8c0d673f28e591fc8dee001eefd7c1c
7
- data.tar.gz: 2baa0d2518993e2e140f5e989ede70a0d9aa19edb2361414bb6375a6161db0424fcbf7ab03f8f8e531ff0cef9160ea2fa0708afb345b6d3f2d2b3dcdeec332fa
6
+ metadata.gz: 3e032eb7438d07371862c7f9015b8c0a2f72dc8a93004387f1b151f75d06614dba89bc97da21d3eb83399f1c86909f1dc0bbd5702ec8cc4fef1f5001f512a3da
7
+ data.tar.gz: e1bd239ec600c28dc5e322b8a8a95daeb539aaada4c9167cf046f5ee5716eb3a6231487b663b534d0db8cb6e1ce2ce53d1dd1b6706184b57f36890edbcbfa663
@@ -23,7 +23,7 @@ jobs:
23
23
  strategy:
24
24
  fail-fast: false
25
25
  matrix:
26
- ruby: [ ruby-head, '3.0', '2.7', '2.6' ]
26
+ ruby: [ ruby-head, '3.1', '3.0', '2.7', '2.6' ]
27
27
  steps:
28
28
  - name: Checkout
29
29
  uses: actions/checkout@v3
data/.rubocop.yml CHANGED
@@ -3,3 +3,6 @@ inherit_gem:
3
3
 
4
4
  AllCops:
5
5
  TargetRubyVersion: 2.6
6
+
7
+ Style/ClassMethodsDefinitions:
8
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Unreleased
2
2
 
3
+ * Introduce `SingleBytePrefixVersionWithStringBypass` (#18, #20).
4
+
3
5
  # 0.7.0
4
6
 
5
7
  * Make ActiveRecordCoder now encode wether records were persisted or not (#12).
data/Gemfile.lock CHANGED
@@ -1,18 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paquito (0.7.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.5)
11
- activesupport (= 6.1.5)
12
- activerecord (6.1.5)
13
- activemodel (= 6.1.5)
14
- activesupport (= 6.1.5)
15
- activesupport (6.1.5)
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.9)
25
- i18n (1.10.0)
24
+ concurrent-ruby (1.1.10)
25
+ i18n (1.12.0)
26
26
  concurrent-ruby (~> 1.0)
27
- minitest (5.15.0)
28
- msgpack (1.5.4)
29
- parallel (1.21.0)
30
- parser (3.1.1.0)
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.2.1)
34
+ regexp_parser (2.5.0)
35
35
  rexml (3.2.5)
36
- rubocop (1.25.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.15.1, < 2.0)
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.16.0)
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.9742)
51
- sqlite3 (1.4.2)
52
- tzinfo (2.0.4)
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.1.0)
55
- zeitwerk (2.5.4)
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` provies utility classes to define optimized and evolutive serializers.
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
- if name == "Time" && defined?(ActiveSupport::TimeWithZone)
213
- name = "ActiveSupport::TimeWithZone" if type == ActiveSupport::TimeWithZone
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Paquito
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
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.7.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-08-08 00:00:00.000000000 Z
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