ciborium 0.1.0-arm-linux-gnu

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +9 -0
  3. data/LICENSE.txt +21 -0
  4. data/NOTICE.md +26 -0
  5. data/README.md +221 -0
  6. data/Rakefile +46 -0
  7. data/lib/ciborium/3.3/ciborium_native.so +0 -0
  8. data/lib/ciborium/3.4/ciborium_native.so +0 -0
  9. data/lib/ciborium/4.0/ciborium_native.so +0 -0
  10. data/lib/ciborium/compat/buffer.rb +190 -0
  11. data/lib/ciborium/compat/errors.rb +30 -0
  12. data/lib/ciborium/compat/packer.rb +90 -0
  13. data/lib/ciborium/compat/unpacker.rb +111 -0
  14. data/lib/ciborium/compat.rb +110 -0
  15. data/lib/ciborium/decode_options.rb +61 -0
  16. data/lib/ciborium/decoder.rb +141 -0
  17. data/lib/ciborium/encode_options.rb +41 -0
  18. data/lib/ciborium/encoded.rb +10 -0
  19. data/lib/ciborium/encoder.rb +80 -0
  20. data/lib/ciborium/errors.rb +25 -0
  21. data/lib/ciborium/extensions/bignum.rb +35 -0
  22. data/lib/ciborium/extensions/regexp.rb +41 -0
  23. data/lib/ciborium/extensions/time.rb +39 -0
  24. data/lib/ciborium/extensions/uri.rb +29 -0
  25. data/lib/ciborium/extensions.rb +92 -0
  26. data/lib/ciborium/native.rb +25 -0
  27. data/lib/ciborium/replace.rb +105 -0
  28. data/lib/ciborium/simple.rb +21 -0
  29. data/lib/ciborium/tagged.rb +18 -0
  30. data/lib/ciborium/version.rb +6 -0
  31. data/lib/ciborium.rb +65 -0
  32. data/licenses/cfg-if-LICENSE-APACHE +201 -0
  33. data/licenses/cfg-if-LICENSE-MIT +25 -0
  34. data/licenses/ciborium-io-LICENSE +201 -0
  35. data/licenses/ciborium-ll-LICENSE +201 -0
  36. data/licenses/half-LICENSE-APACHE +176 -0
  37. data/licenses/half-LICENSE-MIT +19 -0
  38. data/licenses/magnus-LICENSE +21 -0
  39. data/licenses/rb-sys-LICENSE-APACHE +190 -0
  40. data/licenses/rb-sys-LICENSE-MIT +21 -0
  41. data/licenses/zerocopy-LICENSE-APACHE +202 -0
  42. data/licenses/zerocopy-LICENSE-BSD +24 -0
  43. data/licenses/zerocopy-LICENSE-MIT +26 -0
  44. data/sig/generated/ciborium/compat/buffer.rbs +95 -0
  45. data/sig/generated/ciborium/compat/errors.rbs +23 -0
  46. data/sig/generated/ciborium/compat/packer.rbs +53 -0
  47. data/sig/generated/ciborium/compat/unpacker.rbs +59 -0
  48. data/sig/generated/ciborium/compat.rbs +57 -0
  49. data/sig/generated/ciborium/decode_options.rbs +41 -0
  50. data/sig/generated/ciborium/decoder.rbs +85 -0
  51. data/sig/generated/ciborium/encode_options.rbs +33 -0
  52. data/sig/generated/ciborium/encoded.rbs +16 -0
  53. data/sig/generated/ciborium/encoder.rbs +50 -0
  54. data/sig/generated/ciborium/errors.rbs +32 -0
  55. data/sig/generated/ciborium/extensions/bignum.rbs +20 -0
  56. data/sig/generated/ciborium/extensions/regexp.rbs +30 -0
  57. data/sig/generated/ciborium/extensions/time.rbs +22 -0
  58. data/sig/generated/ciborium/extensions/uri.rbs +20 -0
  59. data/sig/generated/ciborium/extensions.rbs +35 -0
  60. data/sig/generated/ciborium/native.rbs +10 -0
  61. data/sig/generated/ciborium/replace.rbs +37 -0
  62. data/sig/generated/ciborium/version.rbs +5 -0
  63. data/sig/generated/ciborium.rbs +30 -0
  64. data/sig/manual/ciborium/extensions.rbs +12 -0
  65. data/sig/manual/ciborium/native.rbs +23 -0
  66. data/sig/manual/ciborium/tagged.rbs +25 -0
  67. metadata +115 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1584c25393390b5075aeb9cc06b332b462fef4e6e21b453748f0437e6074d324
4
+ data.tar.gz: d857cd4785b17a4c0fb45204663f60d682522e2aa5ce8b724f93b97659dc452a
5
+ SHA512:
6
+ metadata.gz: ff4f15ee3f027967ad2c54847b5cdd111c4beed700cc301321a73e7cd9d3e51f63f5f14dbe4fd630f9d92a2d2775cb8f98f21e060d765b5ae32d6d865e214b13
7
+ data.tar.gz: dd2c6bfabc7cd6ce38098d4aaa27bf9904fa9554792a9f3a486bdbf59114a1bc3bc17dca50ec5748a275401f941e5d642aa2c6aa7f92dc7164ef5d5a359f80ba
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-11-11)
4
+
5
+ - Initial release: a CBOR encoder and decoder backed by a Rust extension, where tags carry meaning only through allowlisted extensions.
6
+ - Built-in extensions for bignums, Time, URI and Regexp.
7
+ - `profile: :ctap2` for the CTAP2 canonical CBOR encoding form that WebAuthn uses.
8
+ - `Ciborium::Compat` and `ciborium/replace`, a stand-in for the cbor gem's interface.
9
+ - RBS signatures under `sig/`.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Sorah Fukumori
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/NOTICE.md ADDED
@@ -0,0 +1,26 @@
1
+ # Notices
2
+
3
+ ## Compiled into the native extension
4
+
5
+ | Crate | License |
6
+ |---|---|
7
+ | [ciborium-ll](https://github.com/enarx/ciborium), ciborium-io | Apache-2.0 |
8
+ | [magnus](https://github.com/matsadler/magnus) | MIT |
9
+ | [rb-sys](https://github.com/oxidize-rb/rb-sys) | MIT OR Apache-2.0 |
10
+ | [half](https://github.com/VoidStarKat/half-rs) | MIT OR Apache-2.0 |
11
+ | [cfg-if](https://github.com/rust-lang/cfg-if) | MIT OR Apache-2.0 |
12
+ | [zerocopy](https://github.com/google/zerocopy) | BSD-2-Clause OR Apache-2.0 OR MIT |
13
+
14
+ The source gem builds these from crates.io. Precompiled gems carry their license texts under
15
+ `licenses/`.
16
+
17
+ ## Test fixtures
18
+
19
+ `spec/fixtures/webauthn/` holds attestation objects taken from the test suite of
20
+ [webauthn-ruby](https://github.com/cedarcode/webauthn-ruby) (`spec/support/seeds.rb` at v3.5.0),
21
+ which is available under the MIT License.
22
+
23
+ `spec/upstream/` holds the specs of [cbor-ruby](https://github.com/cabo/cbor-ruby) (the cbor gem,
24
+ `spec/` at commit 89e94f3b059deb58478eab51996f764fcc6ecd82), copied unmodified except for
25
+ `spec_helper.rb`, which is replaced. cbor-ruby is available under the Apache License 2.0, a copy
26
+ of which is in `spec/upstream/LICENSE.txt`.
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # Ciborium: CBOR for Ruby without surprises from the input
2
+
3
+ Ciborium is a [CBOR](https://www.rfc-editor.org/rfc/rfc8949) encoder and decoder for Ruby, backed by a Rust extension built on [ciborium-ll](https://crates.io/crates/ciborium-ll).
4
+
5
+ It also ships a compatibility layer for the [cbor](https://rubygems.org/gems/cbor) gem. `require "ciborium/replace"` makes `CBOR` refer to Ciborium, so gems written against the cbor gem, such as [webauthn](https://github.com/cedarcode/webauthn-ruby) and [cose](https://github.com/cedarcode/cose-ruby), run on Ciborium unchanged.
6
+
7
+ ## Why
8
+
9
+ The cbor gem is a fork of msgpack-ruby's C extension with CBOR support added on top, and has not followed the memory-safety fixes msgpack-ruby made since.
10
+
11
+ Ciborium is a fresh implementation:
12
+
13
+ - **Memory safety**: parsing and serialization happen in Rust. Nesting is bounded by `max_depth` in both directions, and the decoder needs no recursion.
14
+ - **Strict by default**: tags are not interpreted unless an extension is allowlisted, and invalid UTF-8, malformed simple values, duplicate map keys and trailing bytes all raise.
15
+ - **CTAP2 canonical form**: `profile: :ctap2` validates and produces the encoding WebAuthn requires.
16
+ - **RBS signatures** are shipped in `sig/`.
17
+
18
+ ## Installation
19
+
20
+ ```
21
+ bundle add ciborium
22
+ ```
23
+
24
+ Ciborium requires Ruby 3.3 or later.
25
+
26
+ | Platform | Precompiled gem |
27
+ |---|---|
28
+ | Linux x86_64 and aarch64, glibc 2.34 or later | `x86_64-linux-gnu`, `aarch64-linux-gnu` |
29
+ | Linux x86_64 and aarch64, musl | `x86_64-linux-musl`, `aarch64-linux-musl` |
30
+ | Linux armv7, glibc 2.31 or later | `arm-linux-gnu` |
31
+ | macOS 11 or later on Apple silicon | `arm64-darwin` |
32
+ | Windows x64 | `x64-mingw-ucrt` |
33
+
34
+ The precompiled gems carry the extension for Ruby 3.3, 3.4 and 4.0. Anywhere else the source gem builds the extension, which needs a Rust toolchain and libclang (for [rb-sys](https://github.com/oxidize-rb/rb-sys)).
35
+
36
+ ## Usage
37
+
38
+ ```ruby
39
+ require "ciborium"
40
+
41
+ bytes = Ciborium.encode({"name" => "ciborium", "tags" => [1, 2.5, nil]})
42
+ Ciborium.decode(bytes) #=> {"name" => "ciborium", "tags" => [1, 2.5, nil]}
43
+
44
+ Ciborium.decode("\xd8\x23\x63a*b".b) #=> #<data Ciborium::Tagged tag=35, value="a*b">
45
+ ```
46
+
47
+ `decode` raises when bytes follow the item. `decode_prefix` returns the item and the number of bytes it took instead, and `decode_sequence` reads an [RFC 8742](https://www.rfc-editor.org/rfc/rfc8742) sequence:
48
+
49
+ ```ruby
50
+ Ciborium.decode_prefix("\x82\x01\x02\xff".b) #=> [[1, 2], 3]
51
+ Ciborium.decode_sequence("\x01\x02".b) #=> [1, 2]
52
+ ```
53
+
54
+ ### Decoding options
55
+
56
+ ```ruby
57
+ Ciborium.decode(bytes,
58
+ extensions: [], # allowlisted extensions, see below
59
+ symbolize_keys: false, # text map keys as (garbage-collectable) Symbols
60
+ max_depth: 128, # nesting limit for arrays, maps and tags
61
+ duplicate_keys: :error, # or :last_wins
62
+ profile: nil, # or :ctap2
63
+ )
64
+ ```
65
+
66
+ ### Encoding
67
+
68
+ | Ruby | CBOR |
69
+ |---|---|
70
+ | `nil`, `true`, `false` | simple values 22, 21, 20 |
71
+ | `Integer` in `-2**64...2**64` | major type 0 or 1, in the shortest form |
72
+ | `Float` | the narrowest float that keeps the value exactly |
73
+ | `String` in `ASCII-8BIT` | byte string |
74
+ | other `String`, `Symbol` | text string, transcoded to UTF-8 |
75
+ | `Array`, `Hash` | array, map, in insertion order |
76
+ | `Ciborium::Tagged`, `Ciborium::Simple` | tag, simple value |
77
+ | `Ciborium::Encoded` | its bytes as-is, after checking they hold one well-formed item |
78
+
79
+ Anything else raises `Ciborium::EncodeError`, unless an extension encodes it or a `fallback` returns something to encode in its place:
80
+
81
+ ```ruby
82
+ Ciborium.encode(Date.today, fallback: ->(date) { date.iso8601 })
83
+ ```
84
+
85
+ ### Streaming
86
+
87
+ ```ruby
88
+ decoder = Ciborium::Decoder.new(symbolize_keys: true)
89
+ until io.eof?
90
+ decoder.feed(io.readpartial(4096))
91
+ decoder.each { |item| handle(item) } # yields only complete items
92
+ end
93
+
94
+ encoder = Ciborium::Encoder.new
95
+ encoder.write_array_header(2).write(1).write("two")
96
+ encoder.bytes #=> "\x82\x01ctwo"
97
+ ```
98
+
99
+ `Decoder` also reads headers (`read_array_header`, `read_map_header`), and offers `skip` and `next_nil?`.
100
+
101
+ ### Errors
102
+
103
+ | Class | Raised when |
104
+ |---|---|
105
+ | `Ciborium::MalformedError` | the input is not well-formed, breaks a strictness rule or the profile, or has trailing bytes |
106
+ | `Ciborium::IncompleteError` | the input ends inside an item |
107
+ | `Ciborium::DepthError` | nesting exceeds `max_depth` |
108
+ | `Ciborium::ExtensionError` | an extension failed to decode a tag; the original error is the `cause` |
109
+ | `Ciborium::UnexpectedTypeError` | a `Decoder` header reader found another type |
110
+ | `Ciborium::EncodeError` | an object cannot be encoded, or an extension or fallback failed |
111
+
112
+ All of them inherit from `Ciborium::Error`, and the decoding ones also from `Ciborium::DecodeError`.
113
+
114
+ ## Extensions
115
+
116
+ An extension gives a tag meaning in both directions. Pass the built-in ones by name:
117
+
118
+ ```ruby
119
+ Ciborium.decode(bytes, extensions: [:bignum, :time])
120
+ Ciborium.encode(Time.now, extensions: [:time])
121
+ ```
122
+
123
+ | Name | Tags | Ruby |
124
+ |---|---|---|
125
+ | `:bignum` | 2, 3 | `Integer` outside `-2**64...2**64` |
126
+ | `:time` | 0, 1 | `Time`. It encodes as tag 1, using an Integer for whole seconds and a Float otherwise |
127
+ | `:uri` | 32 | `URI::Generic` |
128
+ | `:regexp` | 35 | `Regexp` without flags. `Ciborium::Extensions::Regexp.new(timeout: 0.1)` sets a match timeout |
129
+
130
+ An extension builds Ruby objects from the input, so allowlist only those you trust the input with.
131
+
132
+ Any object that responds to `tags`, `classes`, `decode(tag, value)` and `encode(object)` is an extension too:
133
+
134
+ ```ruby
135
+ class PointExtension
136
+ def tags = [40_000]
137
+ def classes = [Point]
138
+ def decode(_tag, (x, y)) = Point.new(x, y)
139
+ def encode(point) = Ciborium::Tagged.new(40_000, [point.x, point.y])
140
+ end
141
+
142
+ Ciborium.decode(bytes, extensions: [PointExtension.new])
143
+ ```
144
+
145
+ ## WebAuthn and the CTAP2 profile
146
+
147
+ WebAuthn encodes everything in the CTAP2 canonical CBOR encoding form ([CTAP 2.2 section 8](https://fidoalliance.org/specs/fido-v2.2-ps-20250714/fido-client-to-authenticator-protocol-v2.2-ps-20250714.html#ctap2-canonical-cbor-encoding-form)). That form uses no tags at all, so WebAuthn data needs no extension. With `profile: :ctap2`, decoding rejects anything outside that form: tags, indefinite lengths, arguments longer than needed, and map keys that are out of order or duplicated. Encoding sorts map keys into canonical order and refuses tags.
148
+
149
+ ```ruby
150
+ attestation = Ciborium.decode(attestation_object, profile: :ctap2)
151
+
152
+ # The credential public key in authenticator data is followed by extension data.
153
+ cose_key, length = Ciborium.decode_prefix(credential_public_key_and_rest, profile: :ctap2)
154
+ ```
155
+
156
+ ## Replacing the cbor gem
157
+
158
+ `Ciborium::Compat` implements the cbor gem's interface on top of Ciborium:
159
+
160
+ - `CBOR.encode`, `CBOR.decode` and their `pack`, `dump`, `unpack` and `load` aliases
161
+ - `Packer`, `Unpacker` and `Buffer`
162
+ - `Tagged`, `Simple`, its error classes, and `to_cbor`
163
+
164
+ `ciborium/replace` installs it as `CBOR` and marks the cbor gem as loaded, so a later `require "cbor"` does nothing. Keep whatever depends on the cbor gem in the Gemfile, and add ciborium without letting Bundler require it:
165
+
166
+ ```ruby
167
+ # Gemfile
168
+ gem "webauthn"
169
+ gem "ciborium", require: false
170
+ ```
171
+
172
+ Then require `ciborium/replace` after Bundler sets up the load path, and before anything requires `cbor`. In a Rails app, that is between these two lines of `config/application.rb`:
173
+
174
+ ```ruby
175
+ # config/application.rb
176
+ require_relative "boot"
177
+ require "rails/all"
178
+
179
+ require "ciborium/replace"
180
+ Bundler.require(*Rails.groups)
181
+ ```
182
+
183
+ Outside Rails, put it after `require "bundler/setup"` and before `Bundler.require` or the first `require "webauthn"`. If the cbor gem was loaded first anyway, `ciborium/replace` warns and replaces `CBOR` regardless.
184
+
185
+ Because callers of the cbor gem cannot pass options, Compat takes its extensions from one process-wide setting. Nothing is allowlisted by default, so big Integers (tags 2 and 3) decode to `Tagged` until `:bignum` is allowlisted:
186
+
187
+ ```ruby
188
+ Ciborium::Compat.extensions = [:bignum, :time]
189
+ ```
190
+
191
+ The specs of webauthn-ruby and cose-ruby run against Compat in CI with this default, so neither needs any tag extension. The cbor gem's own specs run too, both with no extensions and with every built-in one allowlisted.
192
+
193
+ ### Behaviour of Compat
194
+
195
+ Compat follows Ciborium's rules rather than reproducing the cbor gem exactly:
196
+
197
+ - Tags decode to `Tagged` unless allowlisted in `Compat.extensions`, and Integers outside `-2**64...2**64` encode only with `:bignum` allowlisted.
198
+ - Input that is not well-formed or breaks Ciborium's strictness rules raises `MalformedFormatError`, as does an extension failure, with the original error as the `cause`.
199
+ - Encoding raises `EncodeError` on invalid UTF-8, and on a `Regexp` with flags.
200
+ - `Time` encodes subseconds as a Float.
201
+ - `Tagged` and `Simple` are immutable `Data`.
202
+ - `Unpacker#buffer` holds input that has not yet reached the decoder.
203
+
204
+ ## Development
205
+
206
+ ```
207
+ mise install
208
+ bin/setup
209
+ bundle exec rake compile spec spec:upstream steep
210
+ hk check --all
211
+ ```
212
+
213
+ The Rust extension lives in `ext/ciborium`. Signatures are written inline with rbs-inline and generated into `sig/generated` by `rake rbs`. That directory is committed, and CI checks it is current.
214
+
215
+ ## Contributing
216
+
217
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sorah/ciborium-ruby.
218
+
219
+ ## License
220
+
221
+ MIT License, see [LICENSE.txt](./LICENSE.txt). Third-party code compiled into the extension, and test fixtures taken from other projects, are listed in [NOTICE.md](./NOTICE.md).
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "rake/extensiontask"
6
+
7
+ GEMSPEC = Bundler.load_gemspec("ciborium.gemspec")
8
+
9
+ # No gemspec: precompiled gems are assembled by rakelib/native.rake.
10
+ Rake::ExtensionTask.new("ciborium_native") do |ext|
11
+ ext.ext_dir = "ext/ciborium"
12
+ ext.lib_dir = "lib/ciborium"
13
+ ext.source_pattern = "{Cargo.toml,Cargo.lock,build.rs,src/**/*.rs}"
14
+ # Platforms without a native CI runner are cross-compiled inside rb-sys-dock
15
+ # images (see .github/workflows/native.yml).
16
+ ext.cross_compile = true
17
+ ext.cross_platform = %w[x86_64-linux-musl aarch64-linux-musl arm-linux-gnueabihf]
18
+ end
19
+
20
+ RSpec::Core::RakeTask.new(:spec)
21
+ task spec: :compile
22
+
23
+ namespace :spec do
24
+ desc "Run the cbor gem's specs against Ciborium::Compat, with default and with all extensions"
25
+ task upstream: :compile do
26
+ ["default", "all"].each do |mode|
27
+ sh(
28
+ {"CIBORIUM_UPSTREAM_EXTENSIONS" => mode}, "rspec", "--options", "spec/upstream/.rspec", "-I", "spec/upstream",
29
+ "--default-path", "spec/upstream", "spec/upstream"
30
+ )
31
+ end
32
+ end
33
+ end
34
+
35
+ desc "Regenerate inline RBS signatures under sig/generated"
36
+ task :rbs do
37
+ sh "rbs-inline --output=sig/generated lib"
38
+ sh "rbs -r time -r uri -I sig/generated -I sig/manual -I sig-stub validate"
39
+ end
40
+
41
+ desc "Type check lib against the RBS signatures"
42
+ task steep: :rbs do
43
+ sh "steep check"
44
+ end
45
+
46
+ task default: [:spec, "spec:upstream", :steep]
Binary file
Binary file
Binary file
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ciborium
5
+ module Compat
6
+ # A byte queue in front of an optional IO, as CBOR::Buffer of the cbor gem: reads refill from
7
+ # the IO when the queue runs dry, and writes go out to it once the queue fills.
8
+ class Buffer
9
+ DEFAULT_IO_BUFFER_SIZE = 32 * 1024 #: Integer
10
+
11
+ # @rbs @io: untyped
12
+ # @rbs @io_buffer_size: Integer
13
+ # @rbs @data: String
14
+ # @rbs @pos: Integer
15
+
16
+ attr_reader :io #: untyped
17
+
18
+ # The cbor gem's read_reference_threshold and write_reference_threshold tune its chunk
19
+ # sharing, which this buffer does not do, so they are accepted and ignored.
20
+ #
21
+ #: (?untyped io, ?Hash[Symbol, untyped]? options) -> void
22
+ def initialize(io = nil, options = nil)
23
+ if options.nil? && io.is_a?(Hash)
24
+ options = io
25
+ io = nil
26
+ end
27
+ @io = io
28
+ @io_buffer_size = Integer(options ? options.fetch(:io_buffer_size, DEFAULT_IO_BUFFER_SIZE) : DEFAULT_IO_BUFFER_SIZE)
29
+ @data = "".b
30
+ @pos = 0
31
+ end
32
+
33
+ #: () -> nil
34
+ def clear
35
+ @data = "".b
36
+ @pos = 0
37
+ nil
38
+ end
39
+
40
+ #: () -> Integer
41
+ def size
42
+ @data.bytesize - @pos
43
+ end
44
+
45
+ #: () -> bool
46
+ def empty?
47
+ size.zero?
48
+ end
49
+
50
+ #: (String data) -> Integer
51
+ def write(data)
52
+ @data << data.b
53
+ flush if @io && size >= @io_buffer_size
54
+ data.bytesize
55
+ end
56
+
57
+ #: (String data) -> self
58
+ def <<(data)
59
+ write(data)
60
+ self
61
+ end
62
+
63
+ # Consumes up to n bytes, or everything without n, refilling from the IO until it ends.
64
+ # Returns nil when n is positive and nothing is left.
65
+ #
66
+ #: (?Integer? n, ?String? out) -> String?
67
+ def read(n = nil, out = nil)
68
+ return read_all(nil, out) if n.nil?
69
+ return replace(out, "".b) if n.zero?
70
+
71
+ fill(n)
72
+ return if empty?
73
+
74
+ replace(out, take(n))
75
+ end
76
+
77
+ # Consumes exactly n bytes, or everything without n. Raises EOFError, consuming nothing,
78
+ # when fewer than n bytes are left.
79
+ #
80
+ #: (?Integer? n, ?String? out) -> String
81
+ def read_all(n = nil, out = nil)
82
+ if n.nil?
83
+ fill(nil)
84
+ return replace(out, take(size))
85
+ end
86
+ return replace(out, "".b) if n.zero?
87
+
88
+ fill(n)
89
+ raise ::EOFError, "end of buffer reached" if size < n
90
+
91
+ replace(out, take(n))
92
+ end
93
+
94
+ # Consumes up to n bytes and returns how many it consumed.
95
+ #
96
+ #: (Integer n) -> Integer
97
+ def skip(n)
98
+ fill(n)
99
+ take(n).bytesize
100
+ end
101
+
102
+ # Consumes exactly n bytes, or raises EOFError consuming nothing.
103
+ #
104
+ #: (Integer n) -> self
105
+ def skip_all(n)
106
+ fill(n)
107
+ raise ::EOFError, "end of buffer reached" if size < n
108
+
109
+ take(n)
110
+ self
111
+ end
112
+
113
+ #: () -> String
114
+ def to_str
115
+ @data.byteslice(@pos..) || "".b
116
+ end
117
+
118
+ alias_method :to_s, :to_str
119
+
120
+ #: () -> Array[String]
121
+ def to_a
122
+ [to_str]
123
+ end
124
+
125
+ # Writes the queued bytes to the IO, if there is one.
126
+ #
127
+ #: () -> self
128
+ def flush
129
+ write_to(@io) if @io
130
+ self
131
+ end
132
+
133
+ #: () -> nil
134
+ def close
135
+ @io&.close
136
+ nil
137
+ end
138
+
139
+ # Writes the queued bytes to io and consumes them.
140
+ #
141
+ #: (untyped io) -> Integer
142
+ def write_to(io)
143
+ data = take(size)
144
+ io.write(data)
145
+ data.bytesize
146
+ end
147
+
148
+ #: (Integer? n) -> void
149
+ private def fill(n)
150
+ return unless @io
151
+
152
+ while n.nil? || size < n
153
+ chunk = read_io
154
+ break unless chunk
155
+
156
+ @data << chunk.b
157
+ end
158
+ end
159
+
160
+ #: () -> String?
161
+ private def read_io
162
+ if @io.respond_to?(:readpartial)
163
+ @io.readpartial(@io_buffer_size)
164
+ else
165
+ @io.read(@io_buffer_size)
166
+ end
167
+ rescue ::EOFError
168
+ nil
169
+ end
170
+
171
+ #: (Integer n) -> String
172
+ private def take(n)
173
+ data = @data.byteslice(@pos, n) || "".b
174
+ @pos += data.bytesize
175
+ if @pos == @data.bytesize
176
+ @data = "".b
177
+ @pos = 0
178
+ end
179
+ data
180
+ end
181
+
182
+ #: (String? out, String data) -> String
183
+ private def replace(out, data)
184
+ return data unless out
185
+
186
+ out.replace(data)
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ciborium
5
+ module Compat
6
+ class UnpackError < StandardError; end
7
+
8
+ class MalformedFormatError < UnpackError; end
9
+
10
+ class StackError < UnpackError; end
11
+
12
+ # Raised by Unpacker#read_array_header and #read_map_header on an item of another type.
13
+ class TypeError < StandardError; end
14
+
15
+ # Raises the cbor gem's exception for a Ciborium decoding failure, keeping it as the cause.
16
+ #
17
+ #: [T] () { () -> T } -> T
18
+ def self.translate_errors
19
+ yield
20
+ rescue IncompleteError
21
+ raise ::EOFError, "end of buffer reached"
22
+ rescue DepthError
23
+ raise StackError, "nesting too deep"
24
+ rescue UnexpectedTypeError
25
+ raise TypeError, "unexpected type"
26
+ rescue DecodeError
27
+ raise MalformedFormatError, "malformed CBOR"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ciborium
5
+ module Compat
6
+ # CBOR::Packer of the cbor gem, writing through Ciborium::Encoder.
7
+ class Packer
8
+ # @rbs @buffer: Buffer
9
+ # @rbs @encoder: Ciborium::Encoder
10
+
11
+ attr_reader :buffer #: Buffer
12
+
13
+ #: (?untyped io, ?Hash[Symbol, untyped]? options) -> void
14
+ def initialize(io = nil, options = nil)
15
+ @buffer = Buffer.new(io, options)
16
+ @encoder = Ciborium::Encoder.new(**Compat.encode_options)
17
+ end
18
+
19
+ #: (untyped object) -> self
20
+ def write(object)
21
+ @encoder.write(object)
22
+ drain
23
+ end
24
+
25
+ alias_method :pack, :write
26
+
27
+ #: () -> self
28
+ def write_nil
29
+ write(nil)
30
+ end
31
+
32
+ #: (Integer length) -> self
33
+ def write_array_header(length)
34
+ @encoder.write_array_header(length)
35
+ drain
36
+ end
37
+
38
+ #: (Integer length) -> self
39
+ def write_map_header(length)
40
+ @encoder.write_map_header(length)
41
+ drain
42
+ end
43
+
44
+ #: () -> self
45
+ def flush
46
+ @buffer.flush
47
+ self
48
+ end
49
+
50
+ #: () -> nil
51
+ def clear
52
+ @buffer.clear
53
+ end
54
+
55
+ #: () -> Integer
56
+ def size
57
+ @buffer.size
58
+ end
59
+
60
+ #: () -> bool
61
+ def empty?
62
+ @buffer.empty?
63
+ end
64
+
65
+ #: (untyped io) -> Integer
66
+ def write_to(io)
67
+ @buffer.write_to(io)
68
+ end
69
+
70
+ #: () -> String
71
+ def to_str
72
+ @buffer.to_str
73
+ end
74
+
75
+ alias_method :to_s, :to_str
76
+
77
+ #: () -> Array[String]
78
+ def to_a
79
+ @buffer.to_a
80
+ end
81
+
82
+ #: () -> self
83
+ private def drain
84
+ @buffer << @encoder.bytes
85
+ @encoder.clear
86
+ self
87
+ end
88
+ end
89
+ end
90
+ end