Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,338 @@
|
||||
module cbor
|
||||
|
||||
// Value is the dynamic representation of any CBOR data item. Use it when
|
||||
// the schema isn't known at compile time, when you need to inspect tags,
|
||||
// or when keys aren't strings:
|
||||
//
|
||||
// v := cbor.decode[cbor.Value](bytes)!
|
||||
// match v {
|
||||
// cbor.Text { println(v.value) }
|
||||
// else { ... }
|
||||
// }
|
||||
//
|
||||
// For known schemas prefer `decode[YourStruct]` — it's faster and avoids
|
||||
// the heap allocations of building a Value tree.
|
||||
pub type Value = Array
|
||||
| Bool
|
||||
| Bytes
|
||||
| FloatNum
|
||||
| IntNum
|
||||
| Map
|
||||
| Null
|
||||
| Simple
|
||||
| Tag
|
||||
| Text
|
||||
| Undefined
|
||||
|
||||
// IntNum holds the full unsigned/negative CBOR integer range. CBOR allows
|
||||
// values from -(2^64) to 2^64-1, which exceeds either i64 or u64 alone, so
|
||||
// the sign bit is split out and the magnitude carried as u64.
|
||||
//
|
||||
// for unsigned: negative=false, magnitude=value
|
||||
// for negative: negative=true, magnitude=encoded_argument
|
||||
// actual integer = -1 - i64(magnitude) (when it fits i64)
|
||||
pub struct IntNum {
|
||||
pub:
|
||||
negative bool
|
||||
magnitude u64
|
||||
}
|
||||
|
||||
// Bytes is a CBOR byte string (major type 2).
|
||||
pub struct Bytes {
|
||||
pub mut:
|
||||
data []u8
|
||||
}
|
||||
|
||||
// Text is a CBOR text string (major type 3, valid UTF-8).
|
||||
pub struct Text {
|
||||
pub:
|
||||
value string
|
||||
}
|
||||
|
||||
// Array holds the elements of a CBOR array (major type 4).
|
||||
pub struct Array {
|
||||
pub mut:
|
||||
elements []Value
|
||||
}
|
||||
|
||||
// MapPair represents one key/value entry in a CBOR map. CBOR allows any
|
||||
// data item as a key, so we keep an ordered list of pairs rather than
|
||||
// using V's `map[K]V`.
|
||||
pub struct MapPair {
|
||||
pub:
|
||||
key Value
|
||||
value Value
|
||||
}
|
||||
|
||||
// Map holds the ordered key/value pairs of a CBOR map (major type 5).
|
||||
pub struct Map {
|
||||
pub mut:
|
||||
pairs []MapPair
|
||||
}
|
||||
|
||||
// Tag wraps a tagged data item (major type 6). The content is stored in
|
||||
// a one-element slice rather than as a `&Value` reference: V can box and
|
||||
// recurse a sumtype through a slice, while a direct `&Value` field
|
||||
// requires manual heap allocation. Use `tag.content()` to access it.
|
||||
pub struct Tag {
|
||||
pub:
|
||||
number u64
|
||||
content_box []Value
|
||||
}
|
||||
|
||||
// content returns the Value enclosed by a Tag, or `Null{}` if missing.
|
||||
@[inline]
|
||||
pub fn (t &Tag) content() Value {
|
||||
if t.content_box.len > 0 {
|
||||
return t.content_box[0]
|
||||
}
|
||||
return Null{}
|
||||
}
|
||||
|
||||
// Bool is the wrapped form of CBOR true/false (simple values 20/21).
|
||||
pub struct Bool {
|
||||
pub:
|
||||
value bool
|
||||
}
|
||||
|
||||
// Null is the wrapped form of CBOR null (simple value 22).
|
||||
pub struct Null {}
|
||||
|
||||
// Undefined is the wrapped form of CBOR undefined (simple value 23).
|
||||
pub struct Undefined {}
|
||||
|
||||
// FloatBits records which IEEE 754 width the float was originally encoded
|
||||
// at. The encoder honours this when re-emitting a Value, so round-tripping
|
||||
// preserves the original byte width.
|
||||
pub enum FloatBits as u8 {
|
||||
@none = 0
|
||||
half = 16
|
||||
single = 32
|
||||
double = 64
|
||||
}
|
||||
|
||||
// FloatNum is a CBOR floating-point value (major type 7, additional info
|
||||
// 25/26/27). `bits` records the wire width for fidelity on re-encoding;
|
||||
// the default `.@none` lets the encoder pick the shortest IEEE 754 width
|
||||
// that preserves the value (RFC 8949 §4.2.2 preferred serialisation), so
|
||||
// hand-built FloatNum literals don't accidentally lock in 8-byte output.
|
||||
pub struct FloatNum {
|
||||
pub:
|
||||
value f64
|
||||
bits FloatBits = .@none
|
||||
}
|
||||
|
||||
// Simple is the catch-all for major type 7 simple values 0..255 not
|
||||
// otherwise covered by Bool/Null/Undefined.
|
||||
pub struct Simple {
|
||||
pub:
|
||||
value u8
|
||||
}
|
||||
|
||||
// new_uint wraps a u64 in a Value (unsigned-int variant).
|
||||
@[inline]
|
||||
pub fn new_uint(n u64) Value {
|
||||
return IntNum{
|
||||
negative: false
|
||||
magnitude: n
|
||||
}
|
||||
}
|
||||
|
||||
// new_int wraps a signed i64 in a Value, picking unsigned vs negative.
|
||||
@[inline]
|
||||
pub fn new_int(n i64) Value {
|
||||
if n >= 0 {
|
||||
return IntNum{
|
||||
negative: false
|
||||
magnitude: u64(n)
|
||||
}
|
||||
}
|
||||
return IntNum{
|
||||
negative: true
|
||||
magnitude: u64(-(n + 1))
|
||||
}
|
||||
}
|
||||
|
||||
// new_negative wraps the encoded argument of a major-type-1 value, where
|
||||
// the actual integer is -1 - magnitude. Useful when magnitude exceeds i64.
|
||||
@[inline]
|
||||
pub fn new_negative(magnitude u64) Value {
|
||||
return IntNum{
|
||||
negative: true
|
||||
magnitude: magnitude
|
||||
}
|
||||
}
|
||||
|
||||
// new_text wraps a string as a CBOR text Value.
|
||||
@[inline]
|
||||
pub fn new_text(s string) Value {
|
||||
return Text{
|
||||
value: s
|
||||
}
|
||||
}
|
||||
|
||||
// new_bytes wraps a []u8 as a CBOR byte-string Value.
|
||||
@[inline]
|
||||
pub fn new_bytes(b []u8) Value {
|
||||
return Bytes{
|
||||
data: b
|
||||
}
|
||||
}
|
||||
|
||||
// new_float wraps an f64 as a CBOR FloatNum that re-encodes at full
|
||||
// precision unless `f64_to_half` / f32 conversion is lossless.
|
||||
@[inline]
|
||||
pub fn new_float(v f64) Value {
|
||||
return FloatNum{
|
||||
value: v
|
||||
bits: .@none
|
||||
}
|
||||
}
|
||||
|
||||
// new_tag wraps an existing Value with a tag number.
|
||||
@[inline]
|
||||
pub fn new_tag(number u64, content Value) Value {
|
||||
return Tag{
|
||||
number: number
|
||||
content_box: [content]
|
||||
}
|
||||
}
|
||||
|
||||
// is_nil returns true if v is the CBOR `null` value.
|
||||
@[inline]
|
||||
pub fn (v &Value) is_nil() bool {
|
||||
return v is Null
|
||||
}
|
||||
|
||||
// is_undefined returns true if v is the CBOR `undefined` value.
|
||||
@[inline]
|
||||
pub fn (v &Value) is_undefined() bool {
|
||||
return v is Undefined
|
||||
}
|
||||
|
||||
// as_int returns the value as an i64 when it fits, or none otherwise.
|
||||
// Returns none for FloatNum, Text, etc.
|
||||
//
|
||||
// CBOR negative integers represent `-1 - magnitude`, so magnitude
|
||||
// `2^63 - 1` maps to i64::min and magnitude `2^63` would map to
|
||||
// `-2^63 - 1` — outside i64 range — hence the strict ">" cutoff for
|
||||
// negatives. Use `as_uint` plus the `negative` flag to recover the
|
||||
// full -2^64..2^64-1 CBOR range.
|
||||
pub fn (v &Value) as_int() ?i64 {
|
||||
if v is IntNum {
|
||||
if v.negative {
|
||||
if v.magnitude > u64(max_i64) {
|
||||
return none
|
||||
}
|
||||
return -1 - i64(v.magnitude)
|
||||
}
|
||||
if v.magnitude > u64(max_i64) {
|
||||
return none
|
||||
}
|
||||
return i64(v.magnitude)
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_uint returns the value as a u64 if it's a non-negative integer, else none.
|
||||
pub fn (v &Value) as_uint() ?u64 {
|
||||
if v is IntNum {
|
||||
if v.negative {
|
||||
return none
|
||||
}
|
||||
return v.magnitude
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_float returns the f64 value, or none if v isn't a FloatNum.
|
||||
pub fn (v &Value) as_float() ?f64 {
|
||||
if v is FloatNum {
|
||||
return v.value
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_bool returns the boolean value, or none if v isn't a Bool.
|
||||
pub fn (v &Value) as_bool() ?bool {
|
||||
if v is Bool {
|
||||
return v.value
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_string returns the text-string value, or none if v isn't Text.
|
||||
pub fn (v &Value) as_string() ?string {
|
||||
if v is Text {
|
||||
return v.value
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_bytes returns the byte-string payload, or none if v isn't Bytes.
|
||||
pub fn (v &Value) as_bytes() ?[]u8 {
|
||||
if v is Bytes {
|
||||
return v.data
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_array returns the elements of an Array, or none.
|
||||
pub fn (v &Value) as_array() ?[]Value {
|
||||
if v is Array {
|
||||
return v.elements
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_map returns the pairs of a Map, or none.
|
||||
pub fn (v &Value) as_map() ?[]MapPair {
|
||||
if v is Map {
|
||||
return v.pairs
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// as_tag returns (number, content) of a Tag, or none.
|
||||
pub fn (v &Value) as_tag() ?(u64, Value) {
|
||||
if v is Tag {
|
||||
return v.number, v.content()
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// get does a linear lookup of a string-keyed entry in a Map.
|
||||
// O(n) — for hot paths decode into a typed struct or `map[string]V`.
|
||||
pub fn (v &Value) get(key string) ?Value {
|
||||
if v is Map {
|
||||
for pair in v.pairs {
|
||||
if pair.key is Text {
|
||||
if pair.key.value == key {
|
||||
return pair.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// at returns the element at `index` of an Array.
|
||||
pub fn (v &Value) at(index int) ?Value {
|
||||
if v is Array {
|
||||
if index >= 0 && index < v.elements.len {
|
||||
return v.elements[index]
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// len returns the length of an Array, Map, Text, or Bytes value, or 0.
|
||||
pub fn (v &Value) len() int {
|
||||
match v {
|
||||
Array { return v.elements.len }
|
||||
Map { return v.pairs.len }
|
||||
Text { return v.value.len }
|
||||
Bytes { return v.data.len }
|
||||
else { return 0 }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user