1#![forbid(unsafe_code)]
30
31use std::collections::BTreeMap;
32
33#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum Bencode {
43 Int(i64),
45 Bytes(Vec<u8>),
47 List(Vec<Bencode>),
49 Dict(BTreeMap<Vec<u8>, Bencode>),
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum Error {
57 UnexpectedEnd,
59 UnexpectedByte { offset: usize, byte: u8 },
62 InvalidInteger { offset: usize },
65 IntegerOverflow { offset: usize },
67 InvalidLength { offset: usize },
70 UnsortedOrDuplicateKey { offset: usize },
73 NonStringKey { offset: usize },
75 TrailingData { offset: usize },
77}
78
79impl std::fmt::Display for Error {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Error::UnexpectedEnd => write!(f, "unexpected end of bencode stream"),
83 Error::UnexpectedByte { offset, byte } => {
84 write!(f, "unexpected byte {byte:#04x} at offset {offset}")
85 }
86 Error::InvalidInteger { offset } => write!(f, "invalid integer at offset {offset}"),
87 Error::IntegerOverflow { offset } => write!(f, "integer overflows i64 at offset {offset}"),
88 Error::InvalidLength { offset } => write!(f, "invalid byte-string length at offset {offset}"),
89 Error::UnsortedOrDuplicateKey { offset } => {
90 write!(f, "dict keys not strictly ascending at offset {offset}")
91 }
92 Error::NonStringKey { offset } => write!(f, "non-string dict key at offset {offset}"),
93 Error::TrailingData { offset } => write!(f, "trailing data after value at offset {offset}"),
94 }
95 }
96}
97
98impl std::error::Error for Error {}
99
100pub fn parse(input: &[u8]) -> Result<Bencode, Error> {
107 let (value, rest) = parse_prefix(input)?;
108 if rest.is_empty() {
109 Ok(value)
110 } else {
111 Err(Error::TrailingData { offset: input.len() - rest.len() })
112 }
113}
114
115pub fn parse_prefix(input: &[u8]) -> Result<(Bencode, &[u8]), Error> {
122 Parser { full: input, pos: 0 }.value().map(|v| {
123 (v.0, &input[v.1..])
125 })
126}
127
128struct Parser<'a> {
129 full: &'a [u8],
130 pos: usize,
131}
132
133impl Parser<'_> {
134 fn value(mut self) -> Result<(Bencode, usize), Error> {
136 let v = self.parse_value()?;
137 Ok((v, self.pos))
138 }
139
140 fn peek(&self) -> Result<u8, Error> {
141 self.full.get(self.pos).copied().ok_or(Error::UnexpectedEnd)
142 }
143
144 fn bump(&mut self) -> Result<u8, Error> {
145 let b = self.peek()?;
146 self.pos += 1;
147 Ok(b)
148 }
149
150 fn expect(&mut self, byte: u8) -> Result<(), Error> {
151 let at = self.pos;
152 if self.bump()? == byte {
153 Ok(())
154 } else {
155 Err(Error::UnexpectedByte { offset: at, byte: self.full[at] })
156 }
157 }
158
159 fn parse_value(&mut self) -> Result<Bencode, Error> {
160 match self.peek()? {
161 b'i' => self.parse_int(),
162 b'l' => self.parse_list(),
163 b'd' => self.parse_dict(),
164 b'0'..=b'9' => self.parse_bytes().map(Bencode::Bytes),
165 byte => Err(Error::UnexpectedByte { offset: self.pos, byte }),
166 }
167 }
168
169 fn parse_int(&mut self) -> Result<Bencode, Error> {
170 let start = self.pos;
171 self.expect(b'i')?;
172 let body_start = self.pos;
173 let mut end = self.pos;
175 while self.full.get(end).is_some_and(|&b| b != b'e') {
176 end += 1;
177 }
178 if end >= self.full.len() {
179 return Err(Error::UnexpectedEnd);
180 }
181 let body = &self.full[body_start..end];
182 validate_integer_body(body).map_err(|()| Error::InvalidInteger { offset: start })?;
183 let s = std::str::from_utf8(body).map_err(|_| Error::InvalidInteger { offset: start })?;
184 let n = s.parse::<i64>().map_err(|_| Error::IntegerOverflow { offset: start })?;
185 self.pos = end + 1; Ok(Bencode::Int(n))
187 }
188
189 fn parse_bytes(&mut self) -> Result<Vec<u8>, Error> {
190 let start = self.pos;
191 let mut end = self.pos;
192 while self.full.get(end).is_some_and(u8::is_ascii_digit) {
193 end += 1;
194 }
195 let digits = &self.full[start..end];
196 if digits.is_empty()
198 || (digits.len() > 1 && digits[0] == b'0')
199 {
200 return Err(Error::InvalidLength { offset: start });
201 }
202 let len: usize = std::str::from_utf8(digits)
203 .ok()
204 .and_then(|s| s.parse().ok())
205 .ok_or(Error::InvalidLength { offset: start })?;
206 if self.full.get(end) != Some(&b':') {
207 return Err(Error::InvalidLength { offset: end });
208 }
209 let data_start = end + 1;
210 let data_end = data_start.checked_add(len).ok_or(Error::InvalidLength { offset: start })?;
211 if data_end > self.full.len() {
212 return Err(Error::UnexpectedEnd);
213 }
214 self.pos = data_end;
215 Ok(self.full[data_start..data_end].to_vec())
216 }
217
218 fn parse_list(&mut self) -> Result<Bencode, Error> {
219 self.expect(b'l')?;
220 let mut items = Vec::new();
221 while self.peek()? != b'e' {
222 items.push(self.parse_value()?);
223 }
224 self.pos += 1; Ok(Bencode::List(items))
226 }
227
228 fn parse_dict(&mut self) -> Result<Bencode, Error> {
229 self.expect(b'd')?;
230 let mut map: BTreeMap<Vec<u8>, Bencode> = BTreeMap::new();
231 let mut last_key: Option<Vec<u8>> = None;
232 while self.peek()? != b'e' {
233 let key_offset = self.pos;
234 if !self.peek()?.is_ascii_digit() {
236 return Err(Error::NonStringKey { offset: key_offset });
237 }
238 let key = self.parse_bytes()?;
239 if let Some(prev) = &last_key {
241 if key <= *prev {
242 return Err(Error::UnsortedOrDuplicateKey { offset: key_offset });
243 }
244 }
245 let value = self.parse_value()?;
246 last_key = Some(key.clone());
247 map.insert(key, value);
248 }
249 self.pos += 1; Ok(Bencode::Dict(map))
251 }
252}
253
254fn validate_integer_body(body: &[u8]) -> Result<(), ()> {
257 match body {
258 [] => Err(()),
259 [b'0'] => Ok(()),
260 [b'-', b'0', ..] => Err(()), [b'0', _, ..] => Err(()), _ => {
263 let digits = body.strip_prefix(b"-").unwrap_or(body);
264 if digits.is_empty() || !digits.iter().all(u8::is_ascii_digit) {
265 Err(())
266 } else {
267 Ok(())
268 }
269 }
270 }
271}
272
273impl Bencode {
274 #[must_use]
279 pub fn to_bytes(&self) -> Vec<u8> {
280 let mut out = Vec::new();
281 self.encode_into(&mut out);
282 out
283 }
284
285 fn encode_into(&self, out: &mut Vec<u8>) {
286 match self {
287 Bencode::Int(n) => {
288 out.push(b'i');
289 push_i64(out, *n);
290 out.push(b'e');
291 }
292 Bencode::Bytes(bytes) => {
293 push_usize(out, bytes.len());
294 out.push(b':');
295 out.extend_from_slice(bytes);
296 }
297 Bencode::List(items) => {
298 out.push(b'l');
299 for item in items {
300 item.encode_into(out);
301 }
302 out.push(b'e');
303 }
304 Bencode::Dict(map) => {
305 out.push(b'd');
306 for (key, value) in map {
307 push_usize(out, key.len());
308 out.push(b':');
309 out.extend_from_slice(key);
310 value.encode_into(out);
311 }
312 out.push(b'e');
313 }
314 }
315 }
316
317 #[must_use]
321 pub fn as_int(&self) -> Option<i64> {
322 match self {
323 Bencode::Int(n) => Some(*n),
324 _ => None,
325 }
326 }
327
328 #[must_use]
330 pub fn as_bytes(&self) -> Option<&[u8]> {
331 match self {
332 Bencode::Bytes(b) => Some(b),
333 _ => None,
334 }
335 }
336
337 #[must_use]
339 pub fn as_str(&self) -> Option<&str> {
340 self.as_bytes().and_then(|b| std::str::from_utf8(b).ok())
341 }
342
343 #[must_use]
345 pub fn as_list(&self) -> Option<&[Bencode]> {
346 match self {
347 Bencode::List(items) => Some(items),
348 _ => None,
349 }
350 }
351
352 #[must_use]
354 pub fn as_dict(&self) -> Option<&BTreeMap<Vec<u8>, Bencode>> {
355 match self {
356 Bencode::Dict(map) => Some(map),
357 _ => None,
358 }
359 }
360
361 #[must_use]
364 pub fn get(&self, key: &[u8]) -> Option<&Bencode> {
365 self.as_dict().and_then(|m| m.get(key))
366 }
367}
368
369fn push_i64(out: &mut Vec<u8>, n: i64) {
372 if n < 0 {
373 out.push(b'-');
374 push_u64(out, (n as i128).unsigned_abs() as u64);
376 } else {
377 push_u64(out, n as u64);
378 }
379}
380
381fn push_usize(out: &mut Vec<u8>, n: usize) {
382 push_u64(out, n as u64);
383}
384
385fn push_u64(out: &mut Vec<u8>, n: u64) {
386 if n == 0 {
387 out.push(b'0');
388 return;
389 }
390 let mut buf = [0u8; 20];
393 let mut i = buf.len();
394 let mut v = n;
395 while v > 0 {
396 i -= 1;
397 buf[i] = b'0' + u8::try_from(v % 10).expect("digit 0..=9");
398 v /= 10;
399 }
400 out.extend_from_slice(&buf[i..]);
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406
407 fn dict(pairs: &[(&str, Bencode)]) -> Bencode {
408 Bencode::Dict(pairs.iter().map(|(k, v)| ((*k).as_bytes().to_vec(), v.clone())).collect())
409 }
410 fn bytes(s: &str) -> Bencode {
411 Bencode::Bytes(s.as_bytes().to_vec())
412 }
413
414 #[test]
415 fn parses_the_bep3_examples() {
416 assert_eq!(parse(b"i42e").unwrap(), Bencode::Int(42));
417 assert_eq!(parse(b"i-42e").unwrap(), Bencode::Int(-42));
418 assert_eq!(parse(b"i0e").unwrap(), Bencode::Int(0));
419 assert_eq!(parse(b"4:spam").unwrap(), bytes("spam"));
420 assert_eq!(parse(b"0:").unwrap(), bytes(""));
421 assert_eq!(
422 parse(b"l4:spami42ee").unwrap(),
423 Bencode::List(vec![bytes("spam"), Bencode::Int(42)])
424 );
425 assert_eq!(
426 parse(b"d3:bar4:spam3:fooi42ee").unwrap(),
427 dict(&[("bar", bytes("spam")), ("foo", Bencode::Int(42))])
428 );
429 }
430
431 #[test]
432 fn rejects_malformed_integers() {
433 assert_eq!(parse(b"i03e"), Err(Error::InvalidInteger { offset: 0 }));
434 assert_eq!(parse(b"i-0e"), Err(Error::InvalidInteger { offset: 0 }));
435 assert_eq!(parse(b"ie"), Err(Error::InvalidInteger { offset: 0 }));
436 assert_eq!(parse(b"i12x3e"), Err(Error::InvalidInteger { offset: 0 }));
437 assert!(matches!(parse(b"i99999999999999999999e"), Err(Error::IntegerOverflow { .. })));
438 }
439
440 #[test]
441 fn rejects_malformed_strings_and_dicts() {
442 assert!(matches!(parse(b"01:a"), Err(Error::InvalidLength { .. })));
444 assert_eq!(parse(b"5:abc"), Err(Error::UnexpectedEnd));
446 assert!(matches!(
448 parse(b"d3:fooi1e3:bari2ee"),
449 Err(Error::UnsortedOrDuplicateKey { .. })
450 ));
451 assert!(matches!(
453 parse(b"d1:ai1e1:ai2ee"),
454 Err(Error::UnsortedOrDuplicateKey { .. })
455 ));
456 assert!(matches!(parse(b"di1ei2ee"), Err(Error::NonStringKey { .. })));
458 }
459
460 #[test]
461 fn rejects_trailing_data() {
462 assert!(matches!(parse(b"i1ei2e"), Err(Error::TrailingData { .. })));
463 let (v, rest) = parse_prefix(b"i1ei2e").unwrap();
465 assert_eq!(v, Bencode::Int(1));
466 assert_eq!(rest, b"i2e");
467 }
468
469 #[test]
470 fn encodes_canonically_with_sorted_keys() {
471 let mut map = BTreeMap::new();
473 map.insert(b"foo".to_vec(), Bencode::Int(42));
474 map.insert(b"bar".to_vec(), bytes("spam"));
475 assert_eq!(Bencode::Dict(map).to_bytes(), b"d3:bar4:spam3:fooi42ee");
476 assert_eq!(Bencode::Int(-42).to_bytes(), b"i-42e");
477 assert_eq!(Bencode::Int(0).to_bytes(), b"i0e");
478 assert_eq!(bytes("").to_bytes(), b"0:");
479 }
480
481 #[test]
482 fn encodes_i64_extremes_without_format() {
483 assert_eq!(Bencode::Int(i64::MAX).to_bytes(), b"i9223372036854775807e");
484 assert_eq!(Bencode::Int(i64::MIN).to_bytes(), b"i-9223372036854775808e");
485 }
486
487 proptest::proptest! {
488 #[test]
490 fn parse_of_encode_is_identity(v in arb_bencode(4)) {
491 let bytes = v.to_bytes();
492 proptest::prop_assert_eq!(parse(&bytes).unwrap(), v);
493 }
494
495 #[test]
498 fn encode_of_parse_is_canonical_fixed_point(v in arb_bencode(4)) {
499 let bytes = v.to_bytes();
500 let reparsed = parse(&bytes).unwrap();
501 proptest::prop_assert_eq!(reparsed.to_bytes(), bytes);
502 }
503 }
504
505 fn arb_bencode(depth: u32) -> impl proptest::strategy::Strategy<Value = Bencode> {
507 use proptest::collection::{btree_map, vec};
508 use proptest::prelude::*;
509 let leaf = prop_oneof![
510 any::<i64>().prop_map(Bencode::Int),
511 vec(any::<u8>(), 0..16).prop_map(Bencode::Bytes),
512 ];
513 leaf.prop_recursive(depth, 64, 8, |inner| {
514 prop_oneof![
515 vec(inner.clone(), 0..6).prop_map(Bencode::List),
516 btree_map(vec(any::<u8>(), 0..8), inner, 0..6).prop_map(Bencode::Dict),
517 ]
518 })
519 }
520}