OXYGENHUB

Coverage / crates/oxy-core/src/cbor.rs 98.0%

396 of 404 lines run, 86 of 87 functions · run #336 on paths-for-resolutions-and-amend · run missed

1
//! Deterministic CBOR (RFC 8949 §4.2.1) restricted to the profile of spec §5.1:
2
//! unsigned integers, byte strings, text strings, arrays, maps with unsigned
3
//! integer keys in increasing order, booleans, and null. Everything else is
4
//! rejected on decode and cannot be expressed on encode.
5
6
use std::collections::BTreeMap;
7
8
#[derive(Clone, Debug, PartialEq, Eq)]
9
pub enum Value {
10
    Uint(u64),
11
    Bytes(Vec<u8>),
12
    Text(String),
13
    Array(Vec<Value>),
14
    Map(BTreeMap<u64, Value>),
15
    Bool(bool),
16
    Null,
17
    /// An item already in its canonical encoding, written out as it is.
18
    /// Only built for encoding (never decoded): a large change's inline
19
    /// lines then cost their bytes, not a value per line.
20
    Encoded(Vec<u8>),
21
}
22
23
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
24
pub enum CborError {
25
    #[error("unexpected end of input")]
26
    UnexpectedEnd,
27
    #[error("non-canonical encoding: {0}")]
28
    NonCanonical(&'static str),
29
    #[error("unsupported CBOR item: {0}")]
30
    Unsupported(&'static str),
31
    #[error("trailing bytes after value")]
32
    TrailingBytes,
33
    #[error("invalid UTF-8 in text string")]
34
    InvalidUtf8,
35
    #[error("map keys must be strictly increasing unsigned integers")]
36
    BadMapKey,
37
    #[error("length exceeds input")]
38
    LengthOverflow,
39
}
40
41
impl Value {
4255916×
    pub fn map(pairs: impl IntoIterator<Item = (u64, Value)>) -> Value {
4355916×
        Value::Map(pairs.into_iter().collect())
4455916×
    }
45385334×
    pub fn as_uint(&self) -> Option<u64> {
46385334×
        if let Value::Uint(n) = self {
47385334×
            Some(*n)
48
        } else {
490×
            None
50
        }
51385334×
    }
52419989×
    pub fn as_bytes(&self) -> Option<&[u8]> {
53419989×
        if let Value::Bytes(b) = self {
54419988×
            Some(b)
55
        } else {
561×
            None
57
        }
58419989×
    }
59128869×
    pub fn as_text(&self) -> Option<&str> {
60128869×
        if let Value::Text(t) = self {
61128869×
            Some(t)
62
        } else {
630×
            None
64
        }
65128869×
    }
66287694×
    pub fn as_array(&self) -> Option<&[Value]> {
67287694×
        if let Value::Array(a) = self {
68287694×
            Some(a)
69
        } else {
700×
            None
71
        }
72287694×
    }
73180981×
    pub fn as_map(&self) -> Option<&BTreeMap<u64, Value>> {
74180981×
        if let Value::Map(m) = self {
75180981×
            Some(m)
76
        } else {
770×
            None
78
        }
79180981×
    }
80672×
    pub fn as_bool(&self) -> Option<bool> {
81672×
        if let Value::Bool(b) = self {
82672×
            Some(*b)
83
        } else {
840×
            None
85
        }
86672×
    }
8734039×
    pub fn is_null(&self) -> bool {
8834039×
        matches!(self, Value::Null)
8934039×
    }
90
}
91
9228411×
pub fn encode(v: &Value) -> Vec<u8> {
9328411×
    let mut out = Vec::new();
9428411×
    encode_into(v, &mut out);
9528411×
    out
9628411×
}
97
98640629×
pub fn encode_into(v: &Value, out: &mut Vec<u8>) {
99640629×
    match v {
100170364×
        Value::Uint(n) => head(out, 0, *n),
101175560×
        Value::Bytes(b) => {
102175560×
            head(out, 2, b.len() as u64);
103175560×
            out.extend_from_slice(b);
104175560×
        }
10540333×
        Value::Text(t) => {
10640333×
            head(out, 3, t.len() as u64);
10740333×
            out.extend_from_slice(t.as_bytes());
10840333×
        }
109166998×
        Value::Array(a) => {
110166998×
            head(out, 4, a.len() as u64);
111329149×
            for x in a {
112329149×
                encode_into(x, out);
113329149×
            }
114
        }
11566239×
        Value::Map(m) => {
11666239×
            head(out, 5, m.len() as u64);
117283069×
            for (k, x) in m {
118283069×
                head(out, 0, *k);
119283069×
                encode_into(x, out);
120283069×
            }
121
        }
1221315×
        Value::Bool(false) => out.push(0xf4),
1231490×
        Value::Bool(true) => out.push(0xf5),
12411002×
        Value::Null => out.push(0xf6),
1257328×
        Value::Encoded(b) => out.extend_from_slice(b),
126
    }
127640629×
}
128
129
/// The canonical encoding of an array of byte strings, written without
130
/// building a [`Value`] (large inserts stored as text blobs, spec §6.1).
1317334×
pub fn encode_byte_strings(items: &crate::lines::Lines) -> Vec<u8> {
1327334×
    let mut out = Vec::with_capacity(byte_strings_len(items));
1337334×
    head(&mut out, 4, items.len() as u64);
13417583×
    for b in items.iter() {
13517583×
        head(&mut out, 2, b.len() as u64);
13617583×
        out.extend_from_slice(b);
13717583×
    }
1387334×
    out
1397334×
}
140
141
/// The length of [`encode_byte_strings`]' output, without encoding.
14218797×
pub fn byte_strings_len(items: &crate::lines::Lines) -> usize {
14318797×
    head_len(items.len() as u64)
14418797×
        + items
14518797×
            .iter()
146229671×
            .map(|b| head_len(b.len() as u64) + b.len())
14718797×
            .sum::<usize>()
14818797×
}
149
150248468×
fn head_len(n: u64) -> usize {
151248468×
    match n {
152248468×
        0..=23 => 1,
15316171×
        24..=0xff => 2,
154229×
        0x100..=0xffff => 3,
155128×
        0x1_0000..=0xffff_ffff => 5,
1560×
        _ => 9,
157
    }
158248468×
}
159
160927480×
fn head(out: &mut Vec<u8>, major: u8, n: u64) {
161927480×
    let m = major << 5;
162927480×
    if n < 24 {
163763588×
        out.push(m | n as u8);
164763588×
    } else if n <= 0xff {
165137396×
        out.push(m | 24);
166137396×
        out.push(n as u8);
167137396×
    } else if n <= 0xffff {
16810490×
        out.push(m | 25);
16910490×
        out.extend_from_slice(&(n as u16).to_be_bytes());
17016006×
    } else if n <= 0xffff_ffff {
171201×
        out.push(m | 26);
172201×
        out.extend_from_slice(&(n as u32).to_be_bytes());
17315805×
    } else {
17415805×
        out.push(m | 27);
17515805×
        out.extend_from_slice(&n.to_be_bytes());
17615805×
    }
177927480×
}
178
17976722×
pub fn decode(bytes: &[u8]) -> Result<Value, CborError> {
18076722×
    let mut d = Decoder {
18176722×
        b: bytes,
18276722×
        pos: 0,
18376722×
        depth: 0,
18476722×
    };
18576722×
    let v = d.value()?;
18672149×
    if d.pos != bytes.len() {
187955×
        return Err(CborError::TrailingBytes);
18871194×
    }
18971194×
    Ok(v)
19076722×
}
191
192
const MAX_DEPTH: u32 = 256;
193
194
/// A cursor over canonical CBOR, for decoders that read part of an item
195
/// and skip or defer the rest (history-only loads of large changes).
196
pub struct Cursor<'a> {
197
    d: Decoder<'a>,
198
}
199
200
impl<'a> Cursor<'a> {
20111670×
    pub fn new(bytes: &'a [u8]) -> Cursor<'a> {
20211670×
        Cursor {
20311670×
            d: Decoder {
20411670×
                b: bytes,
20511670×
                pos: 0,
20611670×
                depth: 0,
20711670×
            },
20811670×
        }
20911670×
    }
210
211
    /// The offset of the next item.
21292466×
    pub fn pos(&self) -> usize {
21392466×
        self.d.pos
21492466×
    }
215
216
    /// Whether every byte was read.
21711669×
    pub fn at_end(&self) -> bool {
21811669×
        self.d.pos == self.d.b.len()
21911669×
    }
220
221
    /// The next head: (major type, argument).
222473890×
    pub fn head(&mut self) -> Result<(u8, u64), CborError> {
223473890×
        self.d.head()
224473890×
    }
225
226
    /// The next item, decoded.
227297121×
    pub fn value(&mut self) -> Result<Value, CborError> {
228297121×
        self.d.value()
229297121×
    }
230
231
    /// Step over the next item, checking only its structure.
232274374×
    pub fn skip(&mut self) -> Result<(), CborError> {
233274374×
        let ib = *self.d.b.get(self.d.pos).ok_or(CborError::UnexpectedEnd)?;
234274374×
        if ib >> 5 == 7 {
235236×
            self.d.pos += 1;
236236×
            return Ok(());
237274138×
        }
238274138×
        let (major, n) = self.d.head()?;
239274138×
        match major {
240135894×
            0 => {}
241
            2 | 3 => {
242105206×
                self.d.take(n)?;
243
            }
244
            4 => {
24516722×
                for _ in 0..n {
24631291×
                    self.skip()?;
247
                }
248
            }
249
            5 => {
25016316×
                for _ in 0..n.saturating_mul(2) {
251174804×
                    self.skip()?;
252
                }
253
            }
2540×
            _ => return Err(CborError::Unsupported("negative integer or tag")),
255
        }
256274138×
        Ok(())
257274374×
    }
258
}
259
260
struct Decoder<'a> {
261
    b: &'a [u8],
262
    pos: usize,
263
    depth: u32,
264
}
265
266
impl<'a> Decoder<'a> {
2673140046×
    fn byte(&mut self) -> Result<u8, CborError> {
2683140046×
        let x = *self.b.get(self.pos).ok_or(CborError::UnexpectedEnd)?;
2693139921×
        self.pos += 1;
2703139921×
        Ok(x)
2713140046×
    }
272
273713589×
    fn take(&mut self, n: u64) -> Result<&'a [u8], CborError> {
274713589×
        let n = usize::try_from(n).map_err(|_| CborError::LengthOverflow)?;
275713589×
        let end = self.pos.checked_add(n).ok_or(CborError::LengthOverflow)?;
276713587×
        if end > self.b.len() {
277903×
            return Err(CborError::UnexpectedEnd);
278712684×
        }
279712684×
        let s = &self.b[self.pos..end];
280712684×
        self.pos = end;
281712684×
        Ok(s)
282713589×
    }
283
284
    /// Reads a head for majors 0..=5 and returns (major, argument), enforcing
285
    /// shortest-form arguments.
2862962535×
    fn head(&mut self) -> Result<(u8, u64), CborError> {
2872962535×
        let ib = self.byte()?;
2882962453×
        let major = ib >> 5;
2892962453×
        let ai = ib & 0x1f;
2902962453×
        if major == 7 {
29171×
            return Err(CborError::Unsupported(
29271×
                "simple value or float where a length or integer was expected",
29371×
            ));
2942962382×
        }
2952962382×
        let n = match ai {
2962962382×
            0..=23 => ai as u64,
297
            24 => {
298177511×
                let n = self.byte()? as u64;
299177468×
                if n < 24 {
30020×
                    return Err(CborError::NonCanonical("argument fits in the initial byte"));
301177448×
                }
302177448×
                n
303
            }
304
            25 => {
3059087×
                let n = u16::from_be_bytes(self.take(2)?.try_into().unwrap()) as u64;
3069041×
                if n <= 0xff {
3071×
                    return Err(CborError::NonCanonical("argument fits in one byte"));
3089040×
                }
3099040×
                n
310
            }
311
            26 => {
312325×
                let n = u32::from_be_bytes(self.take(4)?.try_into().unwrap()) as u64;
313302×
                if n <= 0xffff {
3141×
                    return Err(CborError::NonCanonical("argument fits in two bytes"));
315301×
                }
316301×
                n
317
            }
318
            27 => {
31940767×
                let n = u64::from_be_bytes(self.take(8)?.try_into().unwrap());
32040705×
                if n <= 0xffff_ffff {
3211×
                    return Err(CborError::NonCanonical("argument fits in four bytes"));
32240704×
                }
32340704×
                n
324
            }
325473×
            28..=30 => return Err(CborError::Unsupported("reserved additional information")),
326114×
            _ => return Err(CborError::Unsupported("indefinite-length item")),
327
        };
3282961712×
        Ok((major, n))
3292962535×
    }
330
3311527698×
    fn value(&mut self) -> Result<Value, CborError> {
3321527698×
        self.depth += 1;
3331527698×
        if self.depth > MAX_DEPTH {
3341×
            return Err(CborError::Unsupported("nesting deeper than 256"));
3351527697×
        }
3361527697×
        let ib = *self.b.get(self.pos).ok_or(CborError::UnexpectedEnd)?;
3371527026×
        let v = if ib >> 5 == 7 {
33826359×
            self.pos += 1;
33926359×
            match ib {
340597×
                0xf4 => Value::Bool(false),
3411164×
                0xf5 => Value::Bool(true),
34223971×
                0xf6 => Value::Null,
343
                _ => {
344627×
                    return Err(CborError::Unsupported(
345627×
                        "simple value, float, undefined, or break",
346627×
                    ))
347
                }
348
            }
349
        } else {
3501500667×
            let (major, n) = self.head()?;
3511500073×
            match major {
352477496×
                0 => Value::Uint(n),
353446×
                1 => return Err(CborError::Unsupported("negative integer")),
354427109×
                2 => Value::Bytes(self.take(n)?.to_vec()),
355
                3 => Value::Text(
356131095×
                    std::str::from_utf8(self.take(n)?)
357130705×
                        .map_err(|_| CborError::InvalidUtf8)?
358130289×
                        .to_owned(),
359
                ),
360
                4 => {
361295221×
                    let mut a = Vec::new();
362295221×
                    for _ in 0..n {
363440549×
                        a.push(self.value()?);
364
                    }
365294234×
                    Value::Array(a)
366
                }
367
                5 => {
368168196×
                    let mut m = BTreeMap::new();
369168196×
                    let mut last: Option<u64> = None;
370168196×
                    for _ in 0..n {
371713840×
                        let (km, k) = self.head()?;
372713611×
                        if km != 0 {
373291×
                            return Err(CborError::BadMapKey);
374713320×
                        }
375713320×
                        if let Some(l) = last {
376562988×
                            if k <= l {
37714×
                                return Err(CborError::BadMapKey);
378562974×
                            }
379150332×
                        }
380713306×
                        last = Some(k);
381713306×
                        let v = self.value()?;
382713023×
                        m.insert(k, v);
383
                    }
384167379×
                    Value::Map(m)
385
                }
386510×
                _ => return Err(CborError::Unsupported("tag")),
387
            }
388
        };
3891521855×
        self.depth -= 1;
3901521855×
        Ok(v)
3911527698×
    }
392
}
393
394
#[cfg(test)]
395
mod tests {
396
    use super::*;
397
39812×
    fn rt(v: Value) -> Vec<u8> {
39912×
        let b = encode(&v);
40012×
        assert_eq!(decode(&b).unwrap(), v);
40112×
        b
40212×
    }
403
404
    #[test]
4051×
    fn integers_use_shortest_form() {
4061×
        assert_eq!(rt(Value::Uint(0)), [0x00]);
4071×
        assert_eq!(rt(Value::Uint(23)), [0x17]);
4081×
        assert_eq!(rt(Value::Uint(24)), [0x18, 24]);
4091×
        assert_eq!(rt(Value::Uint(255)), [0x18, 255]);
4101×
        assert_eq!(rt(Value::Uint(256)), [0x19, 1, 0]);
4111×
        assert_eq!(rt(Value::Uint(65536)), [0x1a, 0, 1, 0, 0]);
4121×
        assert_eq!(rt(Value::Uint(1 << 32)), [0x1b, 0, 0, 0, 1, 0, 0, 0, 0]);
4131×
        assert_eq!(rt(Value::Uint(u64::MAX)).len(), 9);
4141×
    }
415
416
    #[test]
4171×
    fn rejects_non_shortest_integers() {
4181×
        assert_eq!(
4191×
            decode(&[0x18, 5]),
420
            Err(CborError::NonCanonical("argument fits in the initial byte"))
421
        );
4221×
        assert_eq!(
4231×
            decode(&[0x19, 0, 5]),
424
            Err(CborError::NonCanonical("argument fits in one byte"))
425
        );
4261×
        assert_eq!(
4271×
            decode(&[0x1a, 0, 0, 0, 5]),
428
            Err(CborError::NonCanonical("argument fits in two bytes"))
429
        );
4301×
        assert_eq!(
4311×
            decode(&[0x1b, 0, 0, 0, 0, 0, 0, 0, 5]),
432
            Err(CborError::NonCanonical("argument fits in four bytes"))
433
        );
4341×
    }
435
436
    #[test]
4371×
    fn rejects_unsupported_items() {
4381×
        assert!(matches!(decode(&[0x20]), Err(CborError::Unsupported(_)))); // -1
4391×
        assert!(matches!(
4401×
            decode(&[0xc0, 0x00]),
441
            Err(CborError::Unsupported(_))
442
        )); // tag
4431×
        assert!(matches!(
4441×
            decode(&[0xf9, 0, 0]),
445
            Err(CborError::Unsupported(_))
446
        )); // float16
4471×
        assert!(matches!(
4481×
            decode(&[0x9f, 0xff]),
449
            Err(CborError::Unsupported(_))
450
        )); // indefinite array
4511×
        assert!(matches!(decode(&[0xf7]), Err(CborError::Unsupported(_)))); // undefined
4521×
    }
453
454
    #[test]
4551×
    fn maps_are_sorted_and_unique() {
4561×
        let v = Value::map([
4571×
            (2, Value::Uint(1)),
4581×
            (0, Value::Null),
4591×
            (1, Value::Bool(true)),
4601×
        ]);
4611×
        let b = rt(v);
4621×
        assert_eq!(b, [0xa3, 0x00, 0xf6, 0x01, 0xf5, 0x02, 0x01]);
4631×
        assert_eq!(
4641×
            decode(&[0xa2, 0x01, 0x00, 0x00, 0x00]),
465
            Err(CborError::BadMapKey)
466
        );
4671×
        assert_eq!(
4681×
            decode(&[0xa2, 0x01, 0x00, 0x01, 0x00]),
469
            Err(CborError::BadMapKey)
470
        );
4711×
        assert_eq!(decode(&[0xa1, 0x61, 0x61, 0x00]), Err(CborError::BadMapKey));
472
        // text key
4731×
    }
474
475
    #[test]
4761×
    fn strings_arrays_and_trailing() {
4771×
        rt(Value::Bytes(vec![1, 2, 3]));
4781×
        rt(Value::Text("héllo".into()));
4791×
        rt(Value::Array(vec![
4801×
            Value::Uint(1),
4811×
            Value::Text("x".into()),
4821×
            Value::Array(vec![]),
4831×
        ]));
4841×
        assert_eq!(decode(&[0x00, 0x00]), Err(CborError::TrailingBytes));
4851×
        assert_eq!(decode(&[0x62, 0xff, 0xfe]), Err(CborError::InvalidUtf8));
4861×
        assert_eq!(decode(&[0x43, 1, 2]), Err(CborError::UnexpectedEnd));
4871×
    }
488
489
    #[test]
4901×
    fn huge_length_does_not_allocate() {
4911×
        assert_eq!(
4921×
            decode(&[0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
493
            Err(CborError::LengthOverflow)
494
        );
4951×
        assert!(decode(&[0x9b, 0x00, 0x00, 0x00, 0x01, 0, 0, 0, 0]).is_err());
4961×
    }
497
498171472×
    fn xorshift(state: &mut u64) -> u64 {
499171472×
        *state ^= *state << 13;
500171472×
        *state ^= *state >> 7;
501171472×
        *state ^= *state << 17;
502171472×
        *state
503171472×
    }
504
50511210×
    fn random_value(seed: &mut u64, depth: u32) -> Value {
50611210×
        match xorshift(seed) % if depth > 4 { 5 } else { 7 } {
5071585×
            0 => Value::Uint(match xorshift(seed) % 4 {
508381×
                0 => xorshift(seed) % 24,
509410×
                1 => xorshift(seed) % 256,
510395×
                2 => xorshift(seed) % 65536,
511399×
                _ => xorshift(seed),
512
            }),
5131657×
            1 => Value::Bool(xorshift(seed).is_multiple_of(2)),
5141603×
            2 => Value::Null,
515
            3 => {
5161584×
                let n = (xorshift(seed) % 40) as usize;
51730661×
                Value::Bytes((0..n).map(|_| (xorshift(seed) & 0xff) as u8).collect())
518
            }
519
            4 => {
5201657×
                let n = (xorshift(seed) % 12) as usize;
521
                Value::Text(
5221657×
                    (0..n)
5239255×
                        .map(|_| char::from_u32((xorshift(seed) % 0x24f) as u32 + 1).unwrap_or('x'))
5241657×
                        .collect(),
525
                )
526
            }
527
            5 => {
5281592×
                let n = (xorshift(seed) % 5) as usize;
5293115×
                Value::Array((0..n).map(|_| random_value(seed, depth + 1)).collect())
530
            }
531
            _ => {
5321532×
                let n = (xorshift(seed) % 5) as usize;
5331532×
                let mut m = std::collections::BTreeMap::new();
5343095×
                for _ in 0..n {
5353095×
                    m.insert(xorshift(seed) % 300, random_value(seed, depth + 1));
5363095×
                }
5371532×
                Value::Map(m)
538
            }
539
        }
54011210×
    }
541
542
    /// Random values round-trip and re-encode identically (canonical form).
543
    #[test]
5441×
    fn random_values_round_trip() {
5451×
        let mut seed = 0x2545f4914f6cdd1du64;
5461×
        for _ in 0..2000 {
5472000×
            let v = random_value(&mut seed, 0);
5482000×
            let b = encode(&v);
5492000×
            let back = decode(&b).unwrap();
5502000×
            assert_eq!(back, v);
5512000×
            assert_eq!(encode(&back), b);
552
        }
5531×
    }
554
555
    /// Random bytes and mutated encodings never panic; whatever decodes is
556
    /// canonical, so re-encoding it reproduces the input exactly.
557
    #[test]
5581×
    fn random_and_mutated_input_never_panics() {
5591×
        let mut seed = 0xdeadbeefcafef00du64;
5601×
        for _ in 0..3000 {
5613000×
            let n = (xorshift(&mut seed) % 64) as usize;
56295063×
            let bytes: Vec<u8> = (0..n).map(|_| (xorshift(&mut seed) & 0xff) as u8).collect();
5633000×
            if let Ok(v) = decode(&bytes) {
56413×
                assert_eq!(
56513×
                    encode(&v),
566
                    bytes,
567
                    "accepted input must be canonical: {bytes:02x?}"
568
                );
5692987×
            }
570
        }
5711×
        for _ in 0..3000 {
5723000×
            let v = random_value(&mut seed, 0);
5733000×
            let mut b = encode(&v);
5743000×
            if b.is_empty() {
5750×
                continue;
5763000×
            }
5773000×
            match xorshift(&mut seed) % 3 {
5781003×
                0 => {
5791003×
                    let at = (xorshift(&mut seed) as usize) % b.len();
5801003×
                    b[at] ^= 1 << (xorshift(&mut seed) % 8);
5811003×
                }
5821004×
                1 => {
5831004×
                    let keep = (xorshift(&mut seed) as usize) % b.len();
5841004×
                    b.truncate(keep);
5851004×
                }
586993×
                _ => {
587993×
                    let at = (xorshift(&mut seed) as usize) % b.len();
588993×
                    b.insert(at, (xorshift(&mut seed) & 0xff) as u8);
589993×
                }
590
            }
5913000×
            if let Ok(v2) = decode(&b) {
592479×
                assert_eq!(encode(&v2), b, "accepted mutation must be canonical");
5932521×
            }
594
        }
595
        // Deep nesting is refused rather than overflowing the stack.
5961×
        let mut deep = vec![0x81u8; 100_000];
5971×
        deep.push(0x00);
5981×
        assert!(matches!(decode(&deep), Err(CborError::Unsupported(_))));
599
        // Lengths beyond the input are refused without allocating them.
6001×
        assert!(matches!(
6011×
            decode(&[0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
602
            Err(CborError::LengthOverflow) | Err(CborError::UnexpectedEnd)
603
        ));
6041×
    }
605
}