Skip to content

Commit b20d452

Browse files
committed
Document diagnostic mode
1 parent f416e09 commit b20d452

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
| **Zero-Copy Decoding** | Both eager and lazy decoding operate directly on the input buffer without copying |
1818
| **Lazy Decoding** | `CBOR::Lazy` for on-demand nested access with key and result caching |
1919
| **Streaming** | `CBOR.stream` for CBOR sequences from strings, files, and sockets |
20+
| **Diagnostic Notation** | `CBOR.diagnose` for RFC 8949 §8.1 human-readable output |
2021
| **Class / Module Encoding** | Tag 49999 — classes and modules round-trip automatically |
2122
| **Proc-based Tag Registration** | Register encode/decode procs for any type, including builtins with C state |
2223
| **Performance** | ~30% faster than msgpack; 1.3–3× faster than simdjson for selective access |
@@ -175,6 +176,55 @@ result.equal?(result[0]) # => true (self-referential)
175176

176177
---
177178

179+
### Diagnostic Notation
180+
181+
`CBOR.diagnose` renders any CBOR buffer as RFC 8949 §8.1 diagnostic notation — a human-readable text format useful for debugging, logging, and test output.
182+
183+
```ruby
184+
CBOR.diagnose(CBOR.encode(1)) # => "1"
185+
CBOR.diagnose(CBOR.encode("hello")) # => '"hello"'
186+
CBOR.diagnose(CBOR.encode([1, [2, 3]])) # => "[1,[2,3]]"
187+
CBOR.diagnose(CBOR.encode({ "a" => 1 })) # => '{"a":1}'
188+
CBOR.diagnose(CBOR.encode(true)) # => "true"
189+
CBOR.diagnose(CBOR.encode(nil)) # => "null"
190+
191+
# Tags are rendered as tag(value)
192+
CBOR.diagnose("\xc1\x1a\x51\x4b\x67\xb0") # => "1(1363896240)"
193+
194+
# Byte strings use hex notation
195+
CBOR.diagnose(CBOR.encode("\x01\x02\x03".b)) # => "h'010203'"
196+
```
197+
198+
**Float width suffixes** follow RFC 8610 §8.1: `_1` = f16, `_2` = f32, `_3` = f64. The suffix reflects the wire encoding, not the Ruby type.
199+
200+
```ruby
201+
CBOR.diagnose(CBOR.encode(1.0)) # => "1.0_1" (f16)
202+
CBOR.diagnose(CBOR.encode(1.0e10)) # => "10000000000.0_2" (f32)
203+
CBOR.diagnose(CBOR.encode(3.14)) # => "3.14_3" (f64)
204+
CBOR.diagnose(CBOR.encode(Float::NAN)) # => "NaN_1" (canonical f16)
205+
CBOR.diagnose(CBOR.encode(Float::INFINITY)) # => "Infinity_1"
206+
CBOR.diagnose(CBOR.encode(-Float::INFINITY)) # => "-Infinity_1"
207+
```
208+
209+
**Indefinite-length items** (produced by other encoders) are rendered in diagnostic notation with the `_` prefix per spec:
210+
211+
```ruby
212+
# Indefinite-length array from an external encoder
213+
CBOR.diagnose("\x9f\x01\x02\xff") # => "[_ 1,2]"
214+
215+
# Indefinite-length map
216+
CBOR.diagnose("\xbf\x61a\x01\xff") # => "{_ \"a\":1}"
217+
218+
# Indefinite-length byte string chunks
219+
CBOR.diagnose("\x5f\x41\x01\x41\x02\xff") # => "(_ h'01',h'02')"
220+
```
221+
222+
Note: `CBOR.encode` never produces indefinite-length items. `CBOR.diagnose` can read them from external sources for inspection purposes.
223+
224+
**`MRB_NO_FLOAT` builds** render f16 and f32 values using exact rational arithmetic (no floating-point operations). f64 values use hex-float notation (e.g. `0x1.0p0_3`) as specified in RFC 8610 Appendix G.
225+
226+
---
227+
178228
### Custom Tags & Type Registration
179229

180230
Register your own classes for CBOR encoding/decoding. The `native_ext_type` DSL declares which ivars to encode/decode and their expected Ruby type. Any Ruby class works as a type constraint — including other registered classes, enabling nested structures.

0 commit comments

Comments
 (0)