• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 16646523141

31 Jul 2025 10:28AM UTC coverage: 67.341% (+3.8%) from 63.544%
16646523141

push

github

web-flow
feat(rust/signed-doc): Implement new Catalyst Signed Doc (#338)

* chore: add new line to open pr

Signed-off-by: bkioshn <bkioshn@gmail.com>

* chore: revert

Signed-off-by: bkioshn <bkioshn@gmail.com>

* feat(rust/signed-doc): add new type `DocType` (#339)

* feat(signed-doc): add new type DocType

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): add conversion policy

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): doc type

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): doc type error

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): seperate test

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): format

Signed-off-by: bkioshn <bkioshn@gmail.com>

---------

Signed-off-by: bkioshn <bkioshn@gmail.com>

* feat(rust/signed-doc): Add initial decoding tests for the Catalyst Signed Documents (#349)

* wip

* wip

* fix fmt

* fix spelling

* fix clippy

* fix(rust/signed-doc): Apply new `DocType` (#347)

* feat(signed-doc): add new type DocType

Signed-off-by: bkioshn <bkioshn@gmail.com>

* wip: apply doctype

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): add more function to DocType

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): map old doctype to new doctype

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): add eq to uuidv4

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): fix validator

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): minor fixes

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(catalyst-types): add hash to uuidv4

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): decoding test

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): doctype

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(signed-doc): minor fixes

Signed-off-by: bkioshn <bkioshn@gmail.com>

* chore(sign-doc): fix comment

Signed-off-by: bkioshn <bkioshn@gmail.com>

* fix(catalyst-types): add froms... (continued)

2453 of 2675 new or added lines in 38 files covered. (91.7%)

19 existing lines in 7 files now uncovered.

11312 of 16798 relevant lines covered (67.34%)

2525.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.45
/rust/cbork-utils/src/map.rs
1
//! CBOR map (CBOR major type 5) structure with CBOR decoding and encoding functionality.
2
//! Supports deterministically encoded rules (RFC 8949 Section 4.2) if corresponding
3
//! option is enabled.
4

5
use std::{cmp::Ordering, ops::Deref, vec::IntoIter};
6

7
use crate::{
8
    decode_context::DecodeCtx,
9
    decode_helper::get_bytes,
10
    deterministic_helper::{get_cbor_header_size, get_declared_length, CBOR_MAX_TINY_VALUE},
11
};
12

13
/// Represents a CBOR map key-value pair, preserving original decoding order of values.
14
#[derive(Clone, Debug, PartialEq, Eq)]
15
pub struct Map(Vec<MapEntry>);
16

17
impl Deref for Map {
18
    type Target = Vec<MapEntry>;
19

20
    fn deref(&self) -> &Self::Target {
138✔
21
        &self.0
138✔
22
    }
138✔
23
}
24

25
impl IntoIterator for Map {
26
    type IntoIter = IntoIter<MapEntry>;
27
    type Item = MapEntry;
28

29
    fn into_iter(self) -> Self::IntoIter {
465✔
30
        self.0.into_iter()
465✔
31
    }
465✔
32
}
33

34
/// Represents a CBOR map key-value pair where the key must be deterministically encoded
35
/// according to RFC 8949 Section 4.2.3.
36
///
37
/// This type stores the raw bytes of both key and value to enable:
38
/// 1. Length-first ordering of keys (shorter keys before longer ones)
39
/// 2. Lexicographic comparison of equal-length keys
40
/// 3. Preservation of the original encoded form
41
#[derive(Clone, Eq, PartialEq, Debug)]
42
pub struct MapEntry {
43
    /// Raw bytes of the encoded key, used for deterministic ordering
44
    pub key_bytes: Vec<u8>,
45
    /// Raw bytes of the encoded value
46
    pub value: Vec<u8>,
47
}
48

49
impl PartialOrd for MapEntry {
50
    /// Compare map entries according to RFC 8949 Section 4.2.3 rules:
51
    /// 1. Compare by length of encoded key
52
    /// 2. If lengths equal, compare byte wise lexicographically
53
    ///
54
    /// Returns Some(ordering) since comparison is always defined for these types
55
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
8✔
56
        Some(self.cmp(other))
8✔
57
    }
8✔
58
}
59

60
impl Ord for MapEntry {
61
    /// Compare map entries according to RFC 8949 Section 4.2.3 rules:
62
    /// 1. Compare by length of encoded key
63
    /// 2. If lengths equal, compare byte wise lexicographically
64
    fn cmp(&self, other: &Self) -> Ordering {
19✔
65
        self.key_bytes
19✔
66
            .len()
19✔
67
            .cmp(&other.key_bytes.len())
19✔
68
            .then_with(|| self.key_bytes.cmp(&other.key_bytes))
19✔
69
    }
19✔
70
}
71

72
/// Major type indicator for CBOR maps (major type 5: 101 in top 3 bits)
73
/// As per RFC 8949 Section 4.2.3, maps in deterministic encoding must:
74
/// - Have keys sorted by length first, then byte wise lexicographically
75
/// - Contain no duplicate keys
76
const CBOR_MAJOR_TYPE_MAP: u8 = 5 << 5;
77

78
/// Initial byte for a CBOR map whose length is encoded as an 8-bit unsigned integer
79
/// (uint8).
80
///
81
/// This value combines the map major type (5) with the additional information value (24)
82
/// that indicates a uint8 length follows. The resulting byte is:
83
/// - High 3 bits: 101 (major type 5 for map)
84
/// - Low 5 bits: 24 (indicates uint8 length follows)
85
///
86
/// Used when encoding CBOR maps with lengths between 24 and 255 elements.
87
const CBOR_MAP_LENGTH_UINT8: u8 = CBOR_MAJOR_TYPE_MAP | 24; // For uint8 length encoding
88

89
/// Decodes a CBOR map with deterministic encoding validation (RFC 8949 Section 4.2.3)
90
/// Returns the raw bytes of the map if it passes all deterministic validation rules.
91
///
92
/// From RFC 8949 Section 4.2.3:
93
/// "The keys in every map must be sorted in the following order:
94
///  1. If two keys have different lengths, the shorter one sorts earlier;
95
///  2. If two keys have the same length, the one with the lower value in (byte-wise)
96
///     lexical order sorts earlier."
97
///
98
/// Additionally:
99
/// - Map lengths must use minimal encoding (Section 4.2.1)
100
/// - Indefinite-length maps are not allowed (Section 4.2.2)
101
/// - No two keys may be equal (Section 4.2.3)
102
/// - The keys themselves must be deterministically encoded
103
///
104
/// # Errors
105
///
106
/// Returns `DeterministicError` if:
107
/// - Input is empty (`UnexpectedEof`)
108
/// - Map uses indefinite-length encoding (`IndefiniteLength`)
109
/// - Map length is not encoded minimally (`NonMinimalInt`)
110
/// - Map keys are not properly sorted (`UnorderedMapKeys`)
111
/// - Duplicate keys are found (`DuplicateMapKey`)
112
/// - Map key or value decoding fails (`DecoderError`)
113
impl minicbor::Decode<'_, DecodeCtx> for Map {
114
    fn decode(
618✔
115
        d: &mut minicbor::Decoder<'_>, ctx: &mut DecodeCtx,
618✔
116
    ) -> Result<Self, minicbor::decode::Error> {
618✔
117
        // Capture position before reading the map header
118
        let header_start_pos = d.position();
618✔
119

120
        // From RFC 8949 Section 4.2.2:
121
        // "Indefinite-length items must be made definite-length items."
122
        // The specification explicitly prohibits indefinite-length items in
123
        // deterministic encoding to ensure consistent representation.
124
        let map_len = d.map()?.ok_or_else(|| {
618✔
NEW
125
            minicbor::decode::Error::message(
×
126
                "Indefinite-length items must be made definite-length items",
127
            )
NEW
128
        })?;
×
129

130
        ctx.try_check(|| check_map_minimal_length(d, header_start_pos, map_len))?;
618✔
131

132
        // Decode entries to validate them
133
        let entries = decode_map_entries(d, map_len, ctx)?;
617✔
134

135
        ctx.try_check(|| validate_map_ordering(&entries))?;
616✔
136

137
        Ok(Self(entries))
613✔
138
    }
618✔
139
}
140

141
/// Validates that a CBOR map's length is encoded using the minimal number of bytes as
142
/// required by RFC 8949's deterministic encoding rules.
143
///
144
/// According to the deterministic encoding requirements:
145
/// - The length of a map MUST be encoded using the smallest possible CBOR additional
146
///   information value
147
/// - For values 0 through 23, the additional info byte is used directly
148
/// - For values that fit in 8, 16, 32, or 64 bits, the appropriate multi-byte encoding
149
///   must be used
150
///
151
/// # Specification Reference
152
/// This implementation follows RFC 8949 Section 4.2.1 which requires that:
153
/// "The length of arrays, maps, and strings MUST be encoded using the smallest possible
154
/// CBOR additional information value."
155
fn check_map_minimal_length(
402✔
156
    decoder: &minicbor::Decoder, header_start_pos: usize, value: u64,
402✔
157
) -> Result<(), minicbor::decode::Error> {
402✔
158
    // For zero length, 0xA0 is always the minimal encoding
159
    if value == 0 {
402✔
160
        return Ok(());
224✔
161
    }
178✔
162

163
    let initial_byte = decoder
178✔
164
        .input()
178✔
165
        .get(header_start_pos)
178✔
166
        .copied()
178✔
167
        .ok_or_else(|| {
178✔
NEW
168
            minicbor::decode::Error::message("Cannot read initial byte for minimality check")
×
NEW
169
        })?;
×
170

171
    // Only check minimality for map length encodings using uint8
172
    // Immediate values (0-23) are already minimal by definition
173
    if initial_byte == CBOR_MAP_LENGTH_UINT8 && value <= CBOR_MAX_TINY_VALUE {
178✔
174
        return Err(minicbor::decode::Error::message(
1✔
175
            "map minimal length failure",
1✔
176
        ));
1✔
177
    }
177✔
178

179
    Ok(())
177✔
180
}
402✔
181

182
/// Decodes all key-value pairs in the map
183
fn decode_map_entries(
617✔
184
    d: &mut minicbor::Decoder, length: u64, ctx: &mut DecodeCtx,
617✔
185
) -> Result<Vec<MapEntry>, minicbor::decode::Error> {
617✔
186
    let capacity = usize::try_from(length).map_err(|_| {
617✔
NEW
187
        minicbor::decode::Error::message("Map length too large for current platform")
×
NEW
188
    })?;
×
189
    let mut entries = Vec::with_capacity(capacity);
617✔
190

191
    // Decode each key-value pair
192
    for _ in 0..length {
617✔
193
        // Record the starting position of the key
194
        let key_start = d.position();
955✔
195

196
        // Skip over the key to find its end position
197
        d.skip()?;
955✔
198
        let key_end = d.position();
954✔
199

200
        // Record the starting position of the value
201
        let value_start = d.position();
954✔
202

203
        // Skip over the value to find its end position
204
        d.skip()?;
954✔
205
        let value_end = d.position();
954✔
206

207
        // The keys themselves must be deterministically encoded (4.2.1)
208
        let key_bytes = get_bytes(d, key_start, key_end)?.to_vec();
954✔
209

210
        ctx.try_check(|| map_keys_are_deterministic(&key_bytes))?;
954✔
211

212
        let value = get_bytes(d, value_start, value_end)?.to_vec();
954✔
213

214
        entries.push(MapEntry { key_bytes, value });
954✔
215
    }
216

217
    Ok(entries)
616✔
218
}
617✔
219

220
/// Validates that a CBOR map key follows the deterministic encoding rules as specified in
221
/// RFC 8949. In this case, it validates that the keys themselves must be
222
/// deterministically encoded (4.2.1).
223
fn map_keys_are_deterministic(key_bytes: &[u8]) -> Result<(), minicbor::decode::Error> {
189✔
224
    // if the map keys are not a txt string or byte string we cannot get a declared length
225
    if let Some(key_declared_length) = get_declared_length(key_bytes)? {
189✔
226
        let header_size = get_cbor_header_size(key_bytes)?;
161✔
227
        let actual_content_size = key_bytes.len().checked_sub(header_size).ok_or_else(|| {
161✔
NEW
228
            minicbor::decode::Error::message("Integer overflow in content size calculation")
×
NEW
229
        })?;
×
230

231
        if key_declared_length != actual_content_size {
161✔
NEW
232
            return Err(minicbor::decode::Error::message(
×
NEW
233
                "Declared length does not match the actual length. Non deterministic map key.",
×
NEW
234
            ));
×
235
        }
161✔
236
    }
28✔
237
    Ok(())
189✔
238
}
189✔
239

240
/// Validates map keys are properly ordered according to RFC 8949 Section 4.2.3
241
/// and checks for duplicate keys
242
fn validate_map_ordering(entries: &[MapEntry]) -> Result<(), minicbor::decode::Error> {
401✔
243
    let mut iter = entries.iter();
401✔
244

245
    // Get the first element if it exists
246
    let Some(mut current) = iter.next() else {
401✔
247
        // Empty slice is valid
248
        return Ok(());
224✔
249
    };
250

251
    // Compare each adjacent pair
252
    for next in iter {
183✔
253
        check_pair_ordering(current, next)?;
10✔
254
        current = next;
6✔
255
    }
256

257
    Ok(())
173✔
258
}
401✔
259

260
/// Checks if two adjacent map entries are in the correct order:
261
/// - Keys must be in ascending order (current < next)
262
/// - Duplicate keys are not allowed (current != next)
263
fn check_pair_ordering(current: &MapEntry, next: &MapEntry) -> Result<(), minicbor::decode::Error> {
10✔
264
    match current.cmp(next) {
10✔
265
        Ordering::Less => Ok(()), // Valid: keys are in ascending order
6✔
266
        Ordering::Equal => Err(minicbor::decode::Error::message("Duplicate map key found")),
1✔
267
        Ordering::Greater => {
268
            Err(minicbor::decode::Error::message(
3✔
269
                "Map keys not in canonical order",
3✔
270
            ))
3✔
271
        },
272
    }
273
}
10✔
274

275
#[cfg(test)]
276
mod tests {
277
    use minicbor::{Decode, Decoder};
278

279
    use super::*;
280

281
    /// Ensures that encoding and decoding a map preserves:
282
    /// - The byte wise lexicographic ordering of keys
283
    /// - The exact byte representation of values
284
    /// - The definite length encoding format
285
    #[test]
286
    fn test_map_bytes_roundtrip() {
1✔
287
        // Create a valid deterministic map encoding
288
        let valid_map = vec![
1✔
289
            0xA2, // Map with 2 pairs
290
            0x42, 0x01, 0x02, // Key 1: 2-byte string
291
            0x41, 0x01, // Value 1: 1-byte string
292
            0x43, 0x01, 0x02, 0x03, // Key 2: 3-byte string
293
            0x41, 0x02, // Value 2: 1-byte string
294
        ];
295

296
        let mut decoder = Decoder::new(&valid_map);
1✔
297
        let result = Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).unwrap();
1✔
298

299
        // Verify we got back exactly the same bytes
300

301
        assert_eq!(
1✔
302
            result,
303
            Map(vec![
1✔
304
                MapEntry {
1✔
305
                    // Key 1: 2-byte string
1✔
306
                    key_bytes: vec![0x42, 0x01, 0x02],
1✔
307
                    // Value 1: 1-byte string
1✔
308
                    value: vec![0x41, 0x01]
1✔
309
                },
1✔
310
                MapEntry {
1✔
311
                    // Key 2: 3-byte string
1✔
312
                    key_bytes: vec![0x43, 0x01, 0x02, 0x03,],
1✔
313
                    // Value 2: 1-byte string
1✔
314
                    value: vec![0x41, 0x02,]
1✔
315
                }
1✔
316
            ])
1✔
317
        );
318
    }
1✔
319

320
    /// Test cases for lexicographic ordering of map keys as specified in RFC 8949 Section
321
    /// 4.2.3.
322
    ///
323
    /// From RFC 8949 Section 4.2.3:
324
    /// "The keys in every map must be sorted in the following order:
325
    ///  1. If two keys have different lengths, the shorter one sorts earlier;
326
    ///  2. If two keys have the same length, the one with the lower value in (byte-wise)
327
    ///     lexical order sorts earlier."
328
    #[test]
329
    fn test_map_lexicographic_ordering() {
1✔
330
        // Test case: Equal length keys must be sorted lexicographically
331
        // This follows rule 2 from RFC 8949 Section 4.2.3 for same-length keys
332
        let valid_map = vec![
1✔
333
            0xA2, // Map with 2 pairs
334
            0x42, 0x01, 0x02, // Key 1: [0x01, 0x02]
335
            0x41, 0x01, // Value 1
336
            0x42, 0x01, 0x03, // Key 2: [0x01, 0x03] (lexicographically larger)
337
            0x41, 0x02, // Value 2
338
        ];
339
        let mut decoder = Decoder::new(&valid_map);
1✔
340
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
341

342
        // Invalid ordering - violates RFC 8949 Section 4.2.3 rule 2:
343
        // "If two keys have the same length, the one with the lower value in
344
        // (byte-wise) lexical order sorts earlier"
345
        let invalid_map = vec![
1✔
346
            0xA2, // Map with 2 pairs
347
            0x42, 0x01, 0x03, // Key 1: [0x01, 0x03]
348
            0x41, 0x01, // Value 1
349
            0x42, 0x01, 0x02, // Key 2: [0x01, 0x02] (should come first)
350
            0x41, 0x02, // Value 2
351
        ];
352
        let mut decoder = Decoder::new(&invalid_map);
1✔
353
        assert!(Map::decode(&mut decoder.clone(), &mut DecodeCtx::Deterministic).is_err());
1✔
354
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::non_deterministic()).is_ok());
1✔
355
    }
1✔
356

357
    /// Test empty map handling - special case mentioned in RFC 8949.
358
    /// An empty map is valid and must still follow length encoding rules
359
    /// from Section 4.2.1.
360
    #[test]
361
    fn test_empty_map() {
1✔
362
        let empty_map = vec![
1✔
363
            0xA0, // Map with 0 pairs - encoded with immediate value as per Section 4.2.1
364
        ];
365
        let mut decoder = Decoder::new(&empty_map);
1✔
366
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
367
    }
1✔
368

369
    /// Test minimal length encoding rules for maps as specified in RFC 8949 Section 4.2.1
370
    ///
371
    /// From RFC 8949 Section 4.2.1 "Integer Encoding":
372
    /// "The following must be encoded only with the shortest form that can represent
373
    /// the value:
374
    ///  1. Integer values in items that use integer encoding
375
    ///  2. The length of arrays, maps, strings, and byte strings
376
    ///
377
    /// Specifically for integers:
378
    ///  * 0 to 23 must be expressed in the same byte as the major type
379
    ///  * 24 to 255 must be expressed only with an additional `uint8_t`
380
    ///  * 256 to 65535 must be expressed only with an additional `uint16_t`
381
    ///  * 65536 to 4294967295 must be expressed only with an additional `uint32_t`"
382
    ///
383
    /// For maps (major type 5), the length must follow these rules. This ensures
384
    /// a deterministic encoding where the same length is always encoded the same way.
385
    /// Test minimal length encoding rules for maps as specified in RFC 8949 Section 4.2.1
386
    ///
387
    /// From RFC 8949 Section 4.2.1:
388
    /// "The length of arrays, maps, strings, and byte strings must be encoded in the
389
    /// smallest possible way. For maps (major type 5), lengths 0-23 must be encoded
390
    /// in the initial byte."
391
    #[test]
392
    fn test_map_minimal_length_encoding() {
1✔
393
        // Test case 1: Valid minimal encoding (length = 1)
394
        let valid_small = vec![
1✔
395
            0xA1, // Map, length 1 (major type 5 with immediate value 1)
396
            0x01, // Key: unsigned int 1
397
            0x02, // Value: unsigned int 2
398
        ];
399
        let mut decoder = Decoder::new(&valid_small);
1✔
400

401
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
402

403
        // Test case 2: Invalid non-minimal encoding (using additional info 24 for length 1)
404
        let invalid_small = vec![
1✔
405
            0xB8, // Map with additional info = 24 (0xa0 | 0x18)
406
            0x01, // Length encoded as uint8 = 1
407
            0x01, // Key: unsigned int 1
408
            0x02, // Value: unsigned int 2
409
        ];
410
        let mut decoder = Decoder::new(&invalid_small);
1✔
411
        assert!(Map::decode(&mut decoder.clone(), &mut DecodeCtx::Deterministic).is_err());
1✔
412
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::non_deterministic()).is_ok());
1✔
413
    }
1✔
414

415
    /// Test handling of complex key structures while maintaining canonical ordering
416
    ///
417
    /// RFC 8949 Section 4.2.3 requires correct ordering regardless of key complexity:
418
    /// "The keys in every map must be sorted [...] Note that this rule allows maps
419
    /// to be deterministically ordered regardless of the specific data model of
420
    /// the key values."
421
    #[test]
422
    fn test_map_complex_keys() {
1✔
423
        // Test nested structures in keys while maintaining order
424
        // Following RFC 8949 Section 4.2.3 length-first rule
425
        let valid_complex = vec![
1✔
426
            0xA2, // Map with 2 pairs
427
            0x42, 0x01, 0x02, // Key 1: simple 2-byte string (shorter, so comes first)
428
            0x41, 0x01, // Value 1
429
            0x44, 0x01, 0x02, 0x03, 0x04, // Key 2: 4-byte string (longer, so comes second)
430
            0x41, 0x02, // Value 2
431
        ];
432
        let mut decoder = Decoder::new(&valid_complex);
1✔
433
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
434
    }
1✔
435

436
    /// Test edge cases for map encoding while maintaining compliance with RFC 8949
437
    ///
438
    /// These cases test boundary conditions that must still follow all rules from
439
    /// Section 4.2:
440
    /// - Minimal length encoding (4.2.1)
441
    /// - No indefinite lengths (4.2.2)
442
    /// - Canonical ordering (4.2.3)
443
    #[test]
444
    fn test_map_edge_cases() {
1✔
445
        // Single entry map - must still follow minimal length encoding rules
446
        let single_entry = vec![
1✔
447
            0xA1, // Map with 1 pair (using immediate value as per Section 4.2.1)
448
            0x41, 0x01, // Key: 1-byte string
449
            0x41, 0x02, // Value: 1-byte string
450
        ];
451
        let mut decoder = Decoder::new(&single_entry);
1✔
452
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
453

454
        // Map with zero-length string key - tests smallest possible key case
455
        // Still must follow sorting rules from Section 4.2.3
456
        let zero_length_key = vec![
1✔
457
            0xA1, // Map with 1 pair
458
            0x40, // Key: 0-byte string (smallest possible key length)
459
            0x41, 0x01, // Value: 1-byte string
460
        ];
461
        let mut decoder = Decoder::new(&zero_length_key);
1✔
462
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
463
    }
1✔
464

465
    /// Test duplicate key detection as required by RFC 8949 Section 4.2.3
466
    ///
467
    /// From RFC 8949 Section 4.2.3:
468
    /// "The keys in every map must be sorted [...] Note that this rule
469
    /// automatically implies that no two keys in a map can be equal (have
470
    /// the same length and the same value)."
471
    #[test]
472
    fn test_duplicate_keys() {
1✔
473
        let map_with_duplicates = vec![
1✔
474
            0xA2, // Map with 2 pairs
475
            0x41, 0x01, // Key 1: 1-byte string [0x01]
476
            0x41, 0x02, // Value 1
477
            0x41, 0x01, // Key 2: same as Key 1 (duplicate - invalid)
478
            0x41, 0x03, // Value 2
479
        ];
480
        let mut decoder = Decoder::new(&map_with_duplicates);
1✔
481
        assert!(Map::decode(&mut decoder.clone(), &mut DecodeCtx::Deterministic).is_err());
1✔
482
        assert!(Map::decode(&mut decoder, &mut DecodeCtx::non_deterministic()).is_ok());
1✔
483
    }
1✔
484

485
    #[test]
486
    fn test_map_entry_ord_comprehensive() {
1✔
487
        // Test 1: Length-first ordering
488
        // According to RFC 8949, shorter keys must come before longer keys
489
        // regardless of their actual byte values
490
        let short_key = MapEntry {
1✔
491
            key_bytes: vec![0x41], // Single byte key
1✔
492
            value: vec![0x01],
1✔
493
        };
1✔
494
        let long_key = MapEntry {
1✔
495
            key_bytes: vec![0x41, 0x42, 0x43], // Three byte key (longer)
1✔
496
            value: vec![0x01],
1✔
497
        };
1✔
498
        // Even though both start with 0x41, the shorter one comes first
499
        assert!(short_key < long_key);
1✔
500
        assert!(long_key > short_key);
1✔
501

502
        // Test 2: Lexicographic ordering for equal-length keys
503
        // When keys have the same length, they are compared byte by byte
504
        // lexicographically (like dictionary ordering)
505
        let key_a = MapEntry {
1✔
506
            key_bytes: vec![0x41, 0x41], // Represents "AA" in ASCII
1✔
507
            value: vec![0x01],
1✔
508
        };
1✔
509
        let key_b = MapEntry {
1✔
510
            key_bytes: vec![0x41, 0x42], // Represents "AB" in ASCII
1✔
511
            value: vec![0x01],
1✔
512
        };
1✔
513
        // "AA" comes before "AB" lexicographically
514
        assert!(key_a < key_b);
1✔
515
        assert!(key_b > key_a);
1✔
516
        assert!(key_a == key_a);
1✔
517

518
        // Test 3: Identical entries (same key AND value)
519
        // Complete MapEntry equality requires both key and value to be identical
520
        let entry1 = MapEntry {
1✔
521
            key_bytes: vec![0x41, 0x42],
1✔
522
            value: vec![0x01],
1✔
523
        };
1✔
524
        let entry2 = MapEntry {
1✔
525
            key_bytes: vec![0x41, 0x42],
1✔
526
            value: vec![0x01], // Same value as entry1
1✔
527
        };
1✔
528
        // These are truly identical entries
529
        assert_eq!(entry1, entry2);
1✔
530

531
        // Test 4: Same key, different values - these are NOT equal
532
        // In CBOR maps, this would represent duplicate keys (invalid)
533
        let entry_v1 = MapEntry {
1✔
534
            key_bytes: vec![0x41, 0x42],
1✔
535
            value: vec![0x01],
1✔
536
        };
1✔
537
        let entry_v2 = MapEntry {
1✔
538
            key_bytes: vec![0x41, 0x42],
1✔
539
            value: vec![0x02], // Different value
1✔
540
        };
1✔
541
        // These entries are NOT equal (different values)
542
        assert_ne!(entry_v1, entry_v2);
1✔
543
        // But they have the same ordering position (same key)
544
        assert_eq!(entry_v1.cmp(&entry_v2), std::cmp::Ordering::Equal);
1✔
545

546
        // Test 5: Empty key vs non-empty key
547
        // Empty keys should come before any non-empty key (shortest length rule)
548
        let empty_key = MapEntry {
1✔
549
            key_bytes: vec![], // Empty key (length 0)
1✔
550
            value: vec![0x01],
1✔
551
        };
1✔
552
        let non_empty_key = MapEntry {
1✔
553
            key_bytes: vec![0x00], // Single null byte (length 1)
1✔
554
            value: vec![0x01],
1✔
555
        };
1✔
556
        // Empty key (length 0) comes before single byte key (length 1)
557
        assert!(empty_key < non_empty_key);
1✔
558

559
        // Test 6: Numerical byte value ordering
560
        // Test that individual byte values are compared correctly (0x00 < 0xFF)
561
        let key_0 = MapEntry {
1✔
562
            key_bytes: vec![0x00], // Null byte
1✔
563
            value: vec![0x01],
1✔
564
        };
1✔
565
        let key_255 = MapEntry {
1✔
566
            key_bytes: vec![0xFF], // Maximum byte value
1✔
567
            value: vec![0x01],
1✔
568
        };
1✔
569
        // 0x00 is numerically less than 0xFF
570
        assert!(key_0 < key_255);
1✔
571

572
        // Test 7: Complex multi-byte lexicographic comparison
573
        // Test lexicographic ordering when keys differ in later bytes
574
        let key_complex1 = MapEntry {
1✔
575
            key_bytes: vec![0x01, 0x02, 0x03], // Differs in last byte (0x03)
1✔
576
            value: vec![0x01],
1✔
577
        };
1✔
578
        let key_complex2 = MapEntry {
1✔
579
            key_bytes: vec![0x01, 0x02, 0x04], // Differs in last byte (0x04)
1✔
580
            value: vec![0x01],
1✔
581
        };
1✔
582
        // First two bytes are identical (0x01, 0x02), so compare third byte: 0x03 < 0x04
583
        assert!(key_complex1 < key_complex2);
1✔
584
    }
1✔
585
    /// An edge case where slice [`Ord`] isn't minimal length byte-wise lexicographic.
586
    #[test]
587
    fn test_map_entry_ord_len_edge_case() {
1✔
588
        // Shorter length key with greater first byte.
589
        let lhs = MapEntry {
1✔
590
            key_bytes: minicbor::to_vec("a").unwrap(),
1✔
591
            value: vec![],
1✔
592
        };
1✔
593
        assert_eq!(lhs.key_bytes, &[97, 97]);
1✔
594

595
        // Longer length key with lesser first byte.
596
        let rhs = MapEntry {
1✔
597
            key_bytes: minicbor::to_vec(65535u32).unwrap(),
1✔
598
            value: vec![],
1✔
599
        };
1✔
600
        assert_eq!(rhs.key_bytes, &[25, 255, 255]);
1✔
601

602
        // Shorter must go first.
603
        assert!(lhs < rhs);
1✔
604
    }
1✔
605
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc