| 1 | |
|
| 2 | |
|
| 3 | |
|
| 4 | |
|
| 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 | | |
| 18 | | |
| 19 | | |
| 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 { |
| 42 | 67292× | pub fn map(pairs: impl IntoIterator<Item = (u64, Value)>) -> Value { |
| 43 | 67292× | Value::Map(pairs.into_iter().collect()) |
| 44 | 67292× | } |
| 45 | 417269× | pub fn as_uint(&self) -> Option<u64> { |
| 46 | 417269× | if let Value::Uint(n) = self { |
| 47 | 417269× | Some(*n) |
| 48 | | } else { |
| 49 | 0× | None |
| 50 | | } |
| 51 | 417269× | } |
| 52 | 464341× | pub fn as_bytes(&self) -> Option<&[u8]> { |
| 53 | 464341× | if let Value::Bytes(b) = self { |
| 54 | 464340× | Some(b) |
| 55 | | } else { |
| 56 | 1× | None |
| 57 | | } |
| 58 | 464341× | } |
| 59 | 147663× | pub fn as_text(&self) -> Option<&str> { |
| 60 | 147663× | if let Value::Text(t) = self { |
| 61 | 147663× | Some(t) |
| 62 | | } else { |
| 63 | 0× | None |
| 64 | | } |
| 65 | 147663× | } |
| 66 | 341493× | pub fn as_array(&self) -> Option<&[Value]> { |
| 67 | 341493× | if let Value::Array(a) = self { |
| 68 | 341493× | Some(a) |
| 69 | | } else { |
| 70 | 0× | None |
| 71 | | } |
| 72 | 341493× | } |
| 73 | 208577× | pub fn as_map(&self) -> Option<&BTreeMap<u64, Value>> { |
| 74 | 208577× | if let Value::Map(m) = self { |
| 75 | 208577× | Some(m) |
| 76 | | } else { |
| 77 | 0× | None |
| 78 | | } |
| 79 | 208577× | } |
| 80 | 1079× | pub fn as_bool(&self) -> Option<bool> { |
| 81 | 1079× | if let Value::Bool(b) = self { |
| 82 | 1079× | Some(*b) |
| 83 | | } else { |
| 84 | 0× | None |
| 85 | | } |
| 86 | 1079× | } |
| 87 | 41301× | pub fn is_null(&self) -> bool { |
| 88 | 41301× | matches!(self, Value::Null) |
| 89 | 41301× | } |
| 90 | | } |
| 91 | |
|
| 92 | 33660× | pub fn encode(v: &Value) -> Vec<u8> { |
| 93 | 33660× | let mut out = Vec::new(); |
| 94 | 33660× | encode_into(v, &mut out); |
| 95 | 33660× | out |
| 96 | 33660× | } |
| 97 | |
|
| 98 | 720377× | pub fn encode_into(v: &Value, out: &mut Vec<u8>) { |
| 99 | 720377× | match v { |
| 100 | 190472× | Value::Uint(n) => head(out, 0, *n), |
| 101 | 194444× | Value::Bytes(b) => { |
| 102 | 194444× | head(out, 2, b.len() as u64); |
| 103 | 194444× | out.extend_from_slice(b); |
| 104 | 194444× | } |
| 105 | 49588× | Value::Text(t) => { |
| 106 | 49588× | head(out, 3, t.len() as u64); |
| 107 | 49588× | out.extend_from_slice(t.as_bytes()); |
| 108 | 49588× | } |
| 109 | 181678× | Value::Array(a) => { |
| 110 | 181678× | head(out, 4, a.len() as u64); |
| 111 | 345167× | for x in a { |
| 112 | 345167× | encode_into(x, out); |
| 113 | 345167× | } |
| 114 | | } |
| 115 | 78921× | Value::Map(m) => { |
| 116 | 78921× | head(out, 5, m.len() as u64); |
| 117 | 341550× | for (k, x) in m { |
| 118 | 341550× | head(out, 0, *k); |
| 119 | 341550× | encode_into(x, out); |
| 120 | 341550× | } |
| 121 | | } |
| 122 | 1331× | Value::Bool(false) => out.push(0xf4), |
| 123 | 1663× | Value::Bool(true) => out.push(0xf5), |
| 124 | 14222× | Value::Null => out.push(0xf6), |
| 125 | 8058× | Value::Encoded(b) => out.extend_from_slice(b), |
| 126 | | } |
| 127 | 720377× | } |
| 128 | |
|
| 129 | |
|
| 130 | |
|
| 131 | 8064× | pub fn encode_byte_strings(items: &crate::lines::Lines) -> Vec<u8> { |
| 132 | 8064× | let mut out = Vec::with_capacity(byte_strings_len(items)); |
| 133 | 8064× | head(&mut out, 4, items.len() as u64); |
| 134 | 18633× | for b in items.iter() { |
| 135 | 18633× | head(&mut out, 2, b.len() as u64); |
| 136 | 18633× | out.extend_from_slice(b); |
| 137 | 18633× | } |
| 138 | 8064× | out |
| 139 | 8064× | } |
| 140 | |
|
| 141 | |
|
| 142 | 20474× | pub fn byte_strings_len(items: &crate::lines::Lines) -> usize { |
| 143 | 20474× | head_len(items.len() as u64) |
| 144 | 20474× | + items |
| 145 | 20474× | .iter() |
| 146 | 237058× | .map(|b| head_len(b.len() as u64) + b.len()) |
| 147 | 20474× | .sum::<usize>() |
| 148 | 20474× | } |
| 149 | |
|
| 150 | 257532× | fn head_len(n: u64) -> usize { |
| 151 | 257532× | match n { |
| 152 | 257532× | 0..=23 => 1, |
| 153 | 16229× | 24..=0xff => 2, |
| 154 | 255× | 0x100..=0xffff => 3, |
| 155 | 153× | 0x1_0000..=0xffff_ffff => 5, |
| 156 | 0× | _ => 9, |
| 157 | | } |
| 158 | 257532× | } |
| 159 | |
|
| 160 | 1063350× | fn head(out: &mut Vec<u8>, major: u8, n: u64) { |
| 161 | 1063350× | let m = major << 5; |
| 162 | 1063350× | if n < 24 { |
| 163 | 882042× | out.push(m | n as u8); |
| 164 | 882042× | } else if n <= 0xff { |
| 165 | 148432× | out.push(m | 24); |
| 166 | 148432× | out.push(n as u8); |
| 167 | 148432× | } else if n <= 0xffff { |
| 168 | 12692× | out.push(m | 25); |
| 169 | 12692× | out.extend_from_slice(&(n as u16).to_be_bytes()); |
| 170 | 20184× | } else if n <= 0xffff_ffff { |
| 171 | 310× | out.push(m | 26); |
| 172 | 310× | out.extend_from_slice(&(n as u32).to_be_bytes()); |
| 173 | 19874× | } else { |
| 174 | 19874× | out.push(m | 27); |
| 175 | 19874× | out.extend_from_slice(&n.to_be_bytes()); |
| 176 | 19874× | } |
| 177 | 1063350× | } |
| 178 | |
|
| 179 | 88139× | pub fn decode(bytes: &[u8]) -> Result<Value, CborError> { |
| 180 | 88139× | let mut d = Decoder { |
| 181 | 88139× | b: bytes, |
| 182 | 88139× | pos: 0, |
| 183 | 88139× | depth: 0, |
| 184 | 88139× | }; |
| 185 | 88139× | let v = d.value()?; |
| 186 | 83566× | if d.pos != bytes.len() { |
| 187 | 955× | return Err(CborError::TrailingBytes); |
| 188 | 82611× | } |
| 189 | 82611× | Ok(v) |
| 190 | 88139× | } |
| 191 | |
|
| 192 | | const MAX_DEPTH: u32 = 256; |
| 193 | |
|
| 194 | |
|
| 195 | |
|
| 196 | | pub struct Cursor<'a> { |
| 197 | | d: Decoder<'a>, |
| 198 | | } |
| 199 | |
|
| 200 | | impl<'a> Cursor<'a> { |
| 201 | 14214× | pub fn new(bytes: &'a [u8]) -> Cursor<'a> { |
| 202 | 14214× | Cursor { |
| 203 | 14214× | d: Decoder { |
| 204 | 14214× | b: bytes, |
| 205 | 14214× | pos: 0, |
| 206 | 14214× | depth: 0, |
| 207 | 14214× | }, |
| 208 | 14214× | } |
| 209 | 14214× | } |
| 210 | |
|
| 211 | | |
| 212 | 88310× | pub fn pos(&self) -> usize { |
| 213 | 88310× | self.d.pos |
| 214 | 88310× | } |
| 215 | |
|
| 216 | | |
| 217 | 14213× | pub fn at_end(&self) -> bool { |
| 218 | 14213× | self.d.pos == self.d.b.len() |
| 219 | 14213× | } |
| 220 | |
|
| 221 | | |
| 222 | 492482× | pub fn head(&mut self) -> Result<(u8, u64), CborError> { |
| 223 | 492482× | self.d.head() |
| 224 | 492482× | } |
| 225 | |
|
| 226 | | |
| 227 | 313580× | pub fn value(&mut self) -> Result<Value, CborError> { |
| 228 | 313580× | self.d.value() |
| 229 | 313580× | } |
| 230 | |
|
| 231 | | |
| 232 | 294856× | pub fn skip(&mut self) -> Result<(), CborError> { |
| 233 | 294856× | let ib = *self.d.b.get(self.d.pos).ok_or(CborError::UnexpectedEnd)?; |
| 234 | 294856× | if ib >> 5 == 7 { |
| 235 | 286× | self.d.pos += 1; |
| 236 | 286× | return Ok(()); |
| 237 | 294570× | } |
| 238 | 294570× | let (major, n) = self.d.head()?; |
| 239 | 294570× | match major { |
| 240 | 148741× | 0 => {} |
| 241 | | 2 | 3 => { |
| 242 | 108465× | self.d.take(n)?; |
| 243 | | } |
| 244 | | 4 => { |
| 245 | 19428× | for _ in 0..n { |
| 246 | 35404× | self.skip()?; |
| 247 | | } |
| 248 | | } |
| 249 | | 5 => { |
| 250 | 17936× | for _ in 0..n.saturating_mul(2) { |
| 251 | 191108× | self.skip()?; |
| 252 | | } |
| 253 | | } |
| 254 | 0× | _ => return Err(CborError::Unsupported("negative integer or tag")), |
| 255 | | } |
| 256 | 294570× | Ok(()) |
| 257 | 294856× | } |
| 258 | | } |
| 259 | |
|
| 260 | | struct Decoder<'a> { |
| 261 | | b: &'a [u8], |
| 262 | | pos: usize, |
| 263 | | depth: u32, |
| 264 | | } |
| 265 | |
|
| 266 | | impl<'a> Decoder<'a> { |
| 267 | 3464458× | fn byte(&mut self) -> Result<u8, CborError> { |
| 268 | 3464458× | let x = *self.b.get(self.pos).ok_or(CborError::UnexpectedEnd)?; |
| 269 | 3464333× | self.pos += 1; |
| 270 | 3464333× | Ok(x) |
| 271 | 3464458× | } |
| 272 | |
|
| 273 | 793015× | fn take(&mut self, n: u64) -> Result<&'a [u8], CborError> { |
| 274 | 793015× | let n = usize::try_from(n).map_err(|_| CborError::LengthOverflow)?; |
| 275 | 793015× | let end = self.pos.checked_add(n).ok_or(CborError::LengthOverflow)?; |
| 276 | 793013× | if end > self.b.len() { |
| 277 | 903× | return Err(CborError::UnexpectedEnd); |
| 278 | 792110× | } |
| 279 | 792110× | let s = &self.b[self.pos..end]; |
| 280 | 792110× | self.pos = end; |
| 281 | 792110× | Ok(s) |
| 282 | 793015× | } |
| 283 | |
|
| 284 | | |
| 285 | | |
| 286 | 3253395× | fn head(&mut self) -> Result<(u8, u64), CborError> { |
| 287 | 3253395× | let ib = self.byte()?; |
| 288 | 3253313× | let major = ib >> 5; |
| 289 | 3253313× | let ai = ib & 0x1f; |
| 290 | 3253313× | if major == 7 { |
| 291 | 71× | return Err(CborError::Unsupported( |
| 292 | 71× | "simple value or float where a length or integer was expected", |
| 293 | 71× | )); |
| 294 | 3253242× | } |
| 295 | 3253242× | let n = match ai { |
| 296 | 3253242× | 0..=23 => ai as u64, |
| 297 | | 24 => { |
| 298 | 211063× | let n = self.byte()? as u64; |
| 299 | 211020× | if n < 24 { |
| 300 | 20× | return Err(CborError::NonCanonical("argument fits in the initial byte")); |
| 301 | 211000× | } |
| 302 | 211000× | n |
| 303 | | } |
| 304 | | 25 => { |
| 305 | 12174× | let n = u16::from_be_bytes(self.take(2)?.try_into().unwrap()) as u64; |
| 306 | 12128× | if n <= 0xff { |
| 307 | 1× | return Err(CborError::NonCanonical("argument fits in one byte")); |
| 308 | 12127× | } |
| 309 | 12127× | n |
| 310 | | } |
| 311 | | 26 => { |
| 312 | 419× | let n = u32::from_be_bytes(self.take(4)?.try_into().unwrap()) as u64; |
| 313 | 396× | if n <= 0xffff { |
| 314 | 1× | return Err(CborError::NonCanonical("argument fits in two bytes")); |
| 315 | 395× | } |
| 316 | 395× | n |
| 317 | | } |
| 318 | | 27 => { |
| 319 | 50262× | let n = u64::from_be_bytes(self.take(8)?.try_into().unwrap()); |
| 320 | 50200× | if n <= 0xffff_ffff { |
| 321 | 1× | return Err(CborError::NonCanonical("argument fits in four bytes")); |
| 322 | 50199× | } |
| 323 | 50199× | n |
| 324 | | } |
| 325 | 473× | 28..=30 => return Err(CborError::Unsupported("reserved additional information")), |
| 326 | 114× | _ => return Err(CborError::Unsupported("indefinite-length item")), |
| 327 | | }; |
| 328 | 3252572× | Ok((major, n)) |
| 329 | 3253395× | } |
| 330 | |
|
| 331 | 1699327× | fn value(&mut self) -> Result<Value, CborError> { |
| 332 | 1699327× | self.depth += 1; |
| 333 | 1699327× | if self.depth > MAX_DEPTH { |
| 334 | 1× | return Err(CborError::Unsupported("nesting deeper than 256")); |
| 335 | 1699326× | } |
| 336 | 1699326× | let ib = *self.b.get(self.pos).ok_or(CborError::UnexpectedEnd)?; |
| 337 | 1698655× | let v = if ib >> 5 == 7 { |
| 338 | 32559× | self.pos += 1; |
| 339 | 32559× | match ib { |
| 340 | 613× | 0xf4 => Value::Bool(false), |
| 341 | 1555× | 0xf5 => Value::Bool(true), |
| 342 | 29764× | 0xf6 => Value::Null, |
| 343 | | _ => { |
| 344 | 627× | return Err(CborError::Unsupported( |
| 345 | 627× | "simple value, float, undefined, or break", |
| 346 | 627× | )) |
| 347 | | } |
| 348 | | } |
| 349 | | } else { |
| 350 | 1666096× | let (major, n) = self.head()?; |
| 351 | 1665502× | match major { |
| 352 | 502966× | 0 => Value::Uint(n), |
| 353 | 446× | 1 => return Err(CborError::Unsupported("negative integer")), |
| 354 | 471737× | 2 => Value::Bytes(self.take(n)?.to_vec()), |
| 355 | | 3 => Value::Text( |
| 356 | 149958× | std::str::from_utf8(self.take(n)?) |
| 357 | 149568× | .map_err(|_| CborError::InvalidUtf8)? |
| 358 | 149152× | .to_owned(), |
| 359 | | ), |
| 360 | | 4 => { |
| 361 | 347340× | let mut a = Vec::new(); |
| 362 | 347340× | for _ in 0..n { |
| 363 | 497895× | a.push(self.value()?); |
| 364 | | } |
| 365 | 346353× | Value::Array(a) |
| 366 | | } |
| 367 | | 5 => { |
| 368 | 192545× | let mut m = BTreeMap::new(); |
| 369 | 192545× | let mut last: Option<u64> = None; |
| 370 | 192545× | for _ in 0..n { |
| 371 | 800247× | let (km, k) = self.head()?; |
| 372 | 800018× | if km != 0 { |
| 373 | 291× | return Err(CborError::BadMapKey); |
| 374 | 799727× | } |
| 375 | 799727× | if let Some(l) = last { |
| 376 | 628499× | if k <= l { |
| 377 | 14× | return Err(CborError::BadMapKey); |
| 378 | 628485× | } |
| 379 | 171228× | } |
| 380 | 799713× | last = Some(k); |
| 381 | 799713× | let v = self.value()?; |
| 382 | 799430× | m.insert(k, v); |
| 383 | | } |
| 384 | 191728× | Value::Map(m) |
| 385 | | } |
| 386 | 510× | _ => return Err(CborError::Unsupported("tag")), |
| 387 | | } |
| 388 | | }; |
| 389 | 1693484× | self.depth -= 1; |
| 390 | 1693484× | Ok(v) |
| 391 | 1699327× | } |
| 392 | | } |
| 393 | |
|
| 394 | | #[cfg(test)] |
| 395 | | mod tests { |
| 396 | | use super::*; |
| 397 | |
|
| 398 | 12× | fn rt(v: Value) -> Vec<u8> { |
| 399 | 12× | let b = encode(&v); |
| 400 | 12× | assert_eq!(decode(&b).unwrap(), v); |
| 401 | 12× | b |
| 402 | 12× | } |
| 403 | |
|
| 404 | | #[test] |
| 405 | 1× | fn integers_use_shortest_form() { |
| 406 | 1× | assert_eq!(rt(Value::Uint(0)), [0x00]); |
| 407 | 1× | assert_eq!(rt(Value::Uint(23)), [0x17]); |
| 408 | 1× | assert_eq!(rt(Value::Uint(24)), [0x18, 24]); |
| 409 | 1× | assert_eq!(rt(Value::Uint(255)), [0x18, 255]); |
| 410 | 1× | assert_eq!(rt(Value::Uint(256)), [0x19, 1, 0]); |
| 411 | 1× | assert_eq!(rt(Value::Uint(65536)), [0x1a, 0, 1, 0, 0]); |
| 412 | 1× | assert_eq!(rt(Value::Uint(1 << 32)), [0x1b, 0, 0, 0, 1, 0, 0, 0, 0]); |
| 413 | 1× | assert_eq!(rt(Value::Uint(u64::MAX)).len(), 9); |
| 414 | 1× | } |
| 415 | |
|
| 416 | | #[test] |
| 417 | 1× | fn rejects_non_shortest_integers() { |
| 418 | 1× | assert_eq!( |
| 419 | 1× | decode(&[0x18, 5]), |
| 420 | | Err(CborError::NonCanonical("argument fits in the initial byte")) |
| 421 | | ); |
| 422 | 1× | assert_eq!( |
| 423 | 1× | decode(&[0x19, 0, 5]), |
| 424 | | Err(CborError::NonCanonical("argument fits in one byte")) |
| 425 | | ); |
| 426 | 1× | assert_eq!( |
| 427 | 1× | decode(&[0x1a, 0, 0, 0, 5]), |
| 428 | | Err(CborError::NonCanonical("argument fits in two bytes")) |
| 429 | | ); |
| 430 | 1× | assert_eq!( |
| 431 | 1× | decode(&[0x1b, 0, 0, 0, 0, 0, 0, 0, 5]), |
| 432 | | Err(CborError::NonCanonical("argument fits in four bytes")) |
| 433 | | ); |
| 434 | 1× | } |
| 435 | |
|
| 436 | | #[test] |
| 437 | 1× | fn rejects_unsupported_items() { |
| 438 | 1× | assert!(matches!(decode(&[0x20]), Err(CborError::Unsupported(_)))); |
| 439 | 1× | assert!(matches!( |
| 440 | 1× | decode(&[0xc0, 0x00]), |
| 441 | | Err(CborError::Unsupported(_)) |
| 442 | | )); |
| 443 | 1× | assert!(matches!( |
| 444 | 1× | decode(&[0xf9, 0, 0]), |
| 445 | | Err(CborError::Unsupported(_)) |
| 446 | | )); |
| 447 | 1× | assert!(matches!( |
| 448 | 1× | decode(&[0x9f, 0xff]), |
| 449 | | Err(CborError::Unsupported(_)) |
| 450 | | )); |
| 451 | 1× | assert!(matches!(decode(&[0xf7]), Err(CborError::Unsupported(_)))); |
| 452 | 1× | } |
| 453 | |
|
| 454 | | #[test] |
| 455 | 1× | fn maps_are_sorted_and_unique() { |
| 456 | 1× | let v = Value::map([ |
| 457 | 1× | (2, Value::Uint(1)), |
| 458 | 1× | (0, Value::Null), |
| 459 | 1× | (1, Value::Bool(true)), |
| 460 | 1× | ]); |
| 461 | 1× | let b = rt(v); |
| 462 | 1× | assert_eq!(b, [0xa3, 0x00, 0xf6, 0x01, 0xf5, 0x02, 0x01]); |
| 463 | 1× | assert_eq!( |
| 464 | 1× | decode(&[0xa2, 0x01, 0x00, 0x00, 0x00]), |
| 465 | | Err(CborError::BadMapKey) |
| 466 | | ); |
| 467 | 1× | assert_eq!( |
| 468 | 1× | decode(&[0xa2, 0x01, 0x00, 0x01, 0x00]), |
| 469 | | Err(CborError::BadMapKey) |
| 470 | | ); |
| 471 | 1× | assert_eq!(decode(&[0xa1, 0x61, 0x61, 0x00]), Err(CborError::BadMapKey)); |
| 472 | | |
| 473 | 1× | } |
| 474 | |
|
| 475 | | #[test] |
| 476 | 1× | fn strings_arrays_and_trailing() { |
| 477 | 1× | rt(Value::Bytes(vec![1, 2, 3])); |
| 478 | 1× | rt(Value::Text("héllo".into())); |
| 479 | 1× | rt(Value::Array(vec![ |
| 480 | 1× | Value::Uint(1), |
| 481 | 1× | Value::Text("x".into()), |
| 482 | 1× | Value::Array(vec![]), |
| 483 | 1× | ])); |
| 484 | 1× | assert_eq!(decode(&[0x00, 0x00]), Err(CborError::TrailingBytes)); |
| 485 | 1× | assert_eq!(decode(&[0x62, 0xff, 0xfe]), Err(CborError::InvalidUtf8)); |
| 486 | 1× | assert_eq!(decode(&[0x43, 1, 2]), Err(CborError::UnexpectedEnd)); |
| 487 | 1× | } |
| 488 | |
|
| 489 | | #[test] |
| 490 | 1× | fn huge_length_does_not_allocate() { |
| 491 | 1× | assert_eq!( |
| 492 | 1× | decode(&[0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), |
| 493 | | Err(CborError::LengthOverflow) |
| 494 | | ); |
| 495 | 1× | assert!(decode(&[0x9b, 0x00, 0x00, 0x00, 0x01, 0, 0, 0, 0]).is_err()); |
| 496 | 1× | } |
| 497 | |
|
| 498 | 171472× | fn xorshift(state: &mut u64) -> u64 { |
| 499 | 171472× | *state ^= *state << 13; |
| 500 | 171472× | *state ^= *state >> 7; |
| 501 | 171472× | *state ^= *state << 17; |
| 502 | 171472× | *state |
| 503 | 171472× | } |
| 504 | |
|
| 505 | 11210× | fn random_value(seed: &mut u64, depth: u32) -> Value { |
| 506 | 11210× | match xorshift(seed) % if depth > 4 { 5 } else { 7 } { |
| 507 | 1585× | 0 => Value::Uint(match xorshift(seed) % 4 { |
| 508 | 381× | 0 => xorshift(seed) % 24, |
| 509 | 410× | 1 => xorshift(seed) % 256, |
| 510 | 395× | 2 => xorshift(seed) % 65536, |
| 511 | 399× | _ => xorshift(seed), |
| 512 | | }), |
| 513 | 1657× | 1 => Value::Bool(xorshift(seed).is_multiple_of(2)), |
| 514 | 1603× | 2 => Value::Null, |
| 515 | | 3 => { |
| 516 | 1584× | let n = (xorshift(seed) % 40) as usize; |
| 517 | 30661× | Value::Bytes((0..n).map(|_| (xorshift(seed) & 0xff) as u8).collect()) |
| 518 | | } |
| 519 | | 4 => { |
| 520 | 1657× | let n = (xorshift(seed) % 12) as usize; |
| 521 | | Value::Text( |
| 522 | 1657× | (0..n) |
| 523 | 9255× | .map(|_| char::from_u32((xorshift(seed) % 0x24f) as u32 + 1).unwrap_or('x')) |
| 524 | 1657× | .collect(), |
| 525 | | ) |
| 526 | | } |
| 527 | | 5 => { |
| 528 | 1592× | let n = (xorshift(seed) % 5) as usize; |
| 529 | 3115× | Value::Array((0..n).map(|_| random_value(seed, depth + 1)).collect()) |
| 530 | | } |
| 531 | | _ => { |
| 532 | 1532× | let n = (xorshift(seed) % 5) as usize; |
| 533 | 1532× | let mut m = std::collections::BTreeMap::new(); |
| 534 | 3095× | for _ in 0..n { |
| 535 | 3095× | m.insert(xorshift(seed) % 300, random_value(seed, depth + 1)); |
| 536 | 3095× | } |
| 537 | 1532× | Value::Map(m) |
| 538 | | } |
| 539 | | } |
| 540 | 11210× | } |
| 541 | |
|
| 542 | | |
| 543 | | #[test] |
| 544 | 1× | fn random_values_round_trip() { |
| 545 | 1× | let mut seed = 0x2545f4914f6cdd1du64; |
| 546 | 1× | for _ in 0..2000 { |
| 547 | 2000× | let v = random_value(&mut seed, 0); |
| 548 | 2000× | let b = encode(&v); |
| 549 | 2000× | let back = decode(&b).unwrap(); |
| 550 | 2000× | assert_eq!(back, v); |
| 551 | 2000× | assert_eq!(encode(&back), b); |
| 552 | | } |
| 553 | 1× | } |
| 554 | |
|
| 555 | | |
| 556 | | |
| 557 | | #[test] |
| 558 | 1× | fn random_and_mutated_input_never_panics() { |
| 559 | 1× | let mut seed = 0xdeadbeefcafef00du64; |
| 560 | 1× | for _ in 0..3000 { |
| 561 | 3000× | let n = (xorshift(&mut seed) % 64) as usize; |
| 562 | 95063× | let bytes: Vec<u8> = (0..n).map(|_| (xorshift(&mut seed) & 0xff) as u8).collect(); |
| 563 | 3000× | if let Ok(v) = decode(&bytes) { |
| 564 | 13× | assert_eq!( |
| 565 | 13× | encode(&v), |
| 566 | | bytes, |
| 567 | | "accepted input must be canonical: {bytes:02x?}" |
| 568 | | ); |
| 569 | 2987× | } |
| 570 | | } |
| 571 | 1× | for _ in 0..3000 { |
| 572 | 3000× | let v = random_value(&mut seed, 0); |
| 573 | 3000× | let mut b = encode(&v); |
| 574 | 3000× | if b.is_empty() { |
| 575 | 0× | continue; |
| 576 | 3000× | } |
| 577 | 3000× | match xorshift(&mut seed) % 3 { |
| 578 | 1003× | 0 => { |
| 579 | 1003× | let at = (xorshift(&mut seed) as usize) % b.len(); |
| 580 | 1003× | b[at] ^= 1 << (xorshift(&mut seed) % 8); |
| 581 | 1003× | } |
| 582 | 1004× | 1 => { |
| 583 | 1004× | let keep = (xorshift(&mut seed) as usize) % b.len(); |
| 584 | 1004× | b.truncate(keep); |
| 585 | 1004× | } |
| 586 | 993× | _ => { |
| 587 | 993× | let at = (xorshift(&mut seed) as usize) % b.len(); |
| 588 | 993× | b.insert(at, (xorshift(&mut seed) & 0xff) as u8); |
| 589 | 993× | } |
| 590 | | } |
| 591 | 3000× | if let Ok(v2) = decode(&b) { |
| 592 | 479× | assert_eq!(encode(&v2), b, "accepted mutation must be canonical"); |
| 593 | 2521× | } |
| 594 | | } |
| 595 | | |
| 596 | 1× | let mut deep = vec![0x81u8; 100_000]; |
| 597 | 1× | deep.push(0x00); |
| 598 | 1× | assert!(matches!(decode(&deep), Err(CborError::Unsupported(_)))); |
| 599 | | |
| 600 | 1× | assert!(matches!( |
| 601 | 1× | decode(&[0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), |
| 602 | | Err(CborError::LengthOverflow) | Err(CborError::UnexpectedEnd) |
| 603 | | )); |
| 604 | 1× | } |
| 605 | | } |