1use std::ops::Range;
4
5use crate::limits::AllocationBudget;
6use crate::{Error, ParseLimits, Result};
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct Span {
11 start: usize,
12 end: usize,
13}
14
15impl Span {
16 pub(crate) const fn new(start: usize, end: usize) -> Self {
17 Self { start, end }
18 }
19
20 #[must_use]
22 pub const fn start(self) -> usize {
23 self.start
24 }
25
26 #[must_use]
28 pub const fn end(self) -> usize {
29 self.end
30 }
31
32 #[must_use]
34 pub const fn range(self) -> Range<usize> {
35 self.start..self.end
36 }
37}
38
39#[derive(Clone, Copy, Debug, Eq, PartialEq)]
41pub struct ByteString<'a> {
42 bytes: &'a [u8],
43 span: Span,
44}
45
46impl<'a> ByteString<'a> {
47 #[must_use]
49 pub const fn bytes(self) -> &'a [u8] {
50 self.bytes
51 }
52
53 #[must_use]
55 pub const fn span(self) -> Span {
56 self.span
57 }
58}
59
60#[derive(Debug)]
62pub enum ValueKind<'a> {
63 Integer(Integer<'a>),
65 Bytes(&'a [u8]),
67 List(Vec<Value<'a>>),
69 Dictionary(Vec<(ByteString<'a>, Value<'a>)>),
71}
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub struct Integer<'a> {
76 encoded: &'a [u8],
77}
78
79impl<'a> Integer<'a> {
80 #[must_use]
82 pub const fn encoded(self) -> &'a [u8] {
83 self.encoded
84 }
85
86 #[must_use]
88 pub fn to_i64(self) -> Option<i64> {
89 std::str::from_utf8(self.encoded).ok()?.parse().ok()
90 }
91
92 #[must_use]
94 pub fn to_u64(self) -> Option<u64> {
95 std::str::from_utf8(self.encoded).ok()?.parse().ok()
96 }
97
98 pub(crate) fn canonical_bytes(self) -> Vec<u8> {
99 let (negative, digits) = self
100 .encoded
101 .strip_prefix(b"-")
102 .map_or((false, self.encoded), |digits| (true, digits));
103 let significant = digits
104 .iter()
105 .position(|digit| *digit != b'0')
106 .map_or(&digits[digits.len()..], |start| &digits[start..]);
107 if significant.is_empty() {
108 return b"0".to_vec();
109 }
110 let mut output = Vec::with_capacity(significant.len() + usize::from(negative));
111 if negative {
112 output.push(b'-');
113 }
114 output.extend_from_slice(significant);
115 output
116 }
117}
118
119#[derive(Debug)]
121pub struct Value<'a> {
122 kind: ValueKind<'a>,
123 span: Span,
124}
125
126impl<'a> Value<'a> {
127 #[must_use]
129 pub const fn kind(&self) -> &ValueKind<'a> {
130 &self.kind
131 }
132
133 #[must_use]
135 pub const fn span(&self) -> Span {
136 self.span
137 }
138
139 #[must_use]
141 pub fn as_integer(&self) -> Option<i64> {
142 match self.kind {
143 ValueKind::Integer(value) => value.to_i64(),
144 _ => None,
145 }
146 }
147
148 #[must_use]
150 pub const fn integer(&self) -> Option<Integer<'a>> {
151 match self.kind {
152 ValueKind::Integer(value) => Some(value),
153 _ => None,
154 }
155 }
156
157 #[must_use]
159 pub const fn as_bytes(&self) -> Option<&'a [u8]> {
160 match self.kind {
161 ValueKind::Bytes(bytes) => Some(bytes),
162 _ => None,
163 }
164 }
165
166 #[must_use]
168 pub fn get(&self, key: &[u8]) -> Option<&Value<'a>> {
169 let ValueKind::Dictionary(entries) = &self.kind else {
170 return None;
171 };
172 entries
173 .iter()
174 .find_map(|(candidate, value)| (candidate.bytes == key).then_some(value))
175 }
176}
177
178pub fn parse(input: &[u8]) -> Result<Value<'_>> {
185 parse_with_limits(input, ParseLimits::default())
186}
187
188pub fn parse_with_limits(input: &[u8], limits: ParseLimits) -> Result<Value<'_>> {
195 let mut budget = AllocationBudget::new(limits);
196 parse_with_budget(input, limits, &mut budget)
197}
198
199pub(crate) fn parse_with_budget<'input>(
200 input: &'input [u8],
201 limits: ParseLimits,
202 budget: &mut AllocationBudget,
203) -> Result<Value<'input>> {
204 limits.check_total_input(input.len())?;
205 let mut parser = Parser {
206 input,
207 position: 0,
208 items: 0,
209 limits,
210 budget,
211 };
212 let value = parser.parse_value(0)?;
213 if parser.position != input.len() {
214 return Err(Error::bencode_syntax(
215 parser.position,
216 "trailing bytes after top-level value",
217 ));
218 }
219 Ok(value)
220}
221
222struct Parser<'input, 'budget> {
223 input: &'input [u8],
224 position: usize,
225 items: usize,
226 limits: ParseLimits,
227 budget: &'budget mut AllocationBudget,
228}
229
230impl<'input> Parser<'input, '_> {
231 fn parse_value(&mut self, depth: usize) -> Result<Value<'input>> {
232 self.count_item()?;
233 match self.peek()? {
234 b'i' => self.parse_integer(),
235 b'l' => self.parse_list(depth),
236 b'd' => self.parse_dictionary(depth),
237 b'0'..=b'9' => self.parse_byte_value(),
238 byte => Err(Error::bencode_syntax(
239 self.position,
240 format!("unexpected byte 0x{byte:02x}"),
241 )),
242 }
243 }
244
245 fn parse_integer(&mut self) -> Result<Value<'input>> {
246 let start = self.position;
247 self.position += 1;
248 let number_start = self.position;
249 while let Some(byte) = self.input.get(self.position) {
250 if *byte == b'e' {
251 break;
252 }
253 self.position += 1;
254 }
255 if self.input.get(self.position) != Some(&b'e') {
256 return Err(Error::bencode_syntax(start, "unterminated integer"));
257 }
258 let encoded = &self.input[number_start..self.position];
259 if encoded.is_empty() {
260 return Err(Error::bencode_syntax(number_start, "empty integer"));
261 }
262 if encoded[0] == b'+' {
263 return Err(Error::bencode_syntax(
264 number_start,
265 "plus sign is not allowed",
266 ));
267 }
268 let digits = encoded.strip_prefix(b"-").unwrap_or(encoded);
269 if digits.is_empty() || !digits.iter().all(u8::is_ascii_digit) {
270 return Err(Error::bencode_syntax(
271 number_start,
272 "invalid integer digits",
273 ));
274 }
275 self.limits.check_integer_digits(digits.len())?;
276 self.position += 1;
277 Ok(Value {
278 kind: ValueKind::Integer(Integer { encoded }),
279 span: Span::new(start, self.position),
280 })
281 }
282
283 fn parse_byte_value(&mut self) -> Result<Value<'input>> {
284 let byte_string = self.parse_byte_string(false)?;
285 Ok(Value {
286 kind: ValueKind::Bytes(byte_string.bytes),
287 span: byte_string.span,
288 })
289 }
290
291 fn parse_byte_string(&mut self, count_item: bool) -> Result<ByteString<'input>> {
292 if count_item {
293 self.count_item()?;
294 }
295 let start = self.position;
296 let length_start = self.position;
297 while matches!(self.input.get(self.position), Some(b'0'..=b'9')) {
298 self.position += 1;
299 }
300 if self.position == length_start || self.input.get(self.position) != Some(&b':') {
301 return Err(Error::bencode_syntax(start, "invalid byte-string length"));
302 }
303 let length_text = std::str::from_utf8(&self.input[length_start..self.position])
304 .map_err(|_| Error::bencode_syntax(start, "invalid byte-string length"))?;
305 let length = length_text
306 .parse::<usize>()
307 .map_err(|_| Error::bencode_syntax(start, "byte-string length is out of range"))?;
308 self.limits.check_byte_string_length(length)?;
309 self.position += 1;
310 let end = self
311 .position
312 .checked_add(length)
313 .ok_or_else(|| Error::bencode_syntax(start, "byte-string end offset overflowed"))?;
314 let Some(bytes) = self.input.get(self.position..end) else {
315 return Err(Error::bencode_syntax(start, "truncated byte string"));
316 };
317 self.position = end;
318 Ok(ByteString {
319 bytes,
320 span: Span::new(start, end),
321 })
322 }
323
324 fn parse_list(&mut self, depth: usize) -> Result<Value<'input>> {
325 let container_depth = depth
326 .checked_add(1)
327 .ok_or_else(|| Error::resource_limit("depth", usize::MAX, self.limits.max_depth()))?;
328 self.limits.check_depth(container_depth)?;
329 let start = self.position;
330 self.position += 1;
331 let mut items = Vec::new();
332 loop {
333 match self.input.get(self.position) {
334 Some(b'e') => {
335 self.position += 1;
336 break;
337 }
338 Some(_) => {
339 self.budget.charge(std::mem::size_of::<Value<'input>>())?;
340 items.try_reserve_exact(1).map_err(|_| {
341 Error::resource_limit(
342 "owned allocation",
343 usize::MAX,
344 self.limits.max_owned_allocation(),
345 )
346 })?;
347 items.push(self.parse_value(container_depth)?);
348 }
349 None => return Err(Error::bencode_syntax(start, "unterminated list")),
350 }
351 }
352 Ok(Value {
353 kind: ValueKind::List(items),
354 span: Span::new(start, self.position),
355 })
356 }
357
358 fn parse_dictionary(&mut self, depth: usize) -> Result<Value<'input>> {
359 let container_depth = depth
360 .checked_add(1)
361 .ok_or_else(|| Error::resource_limit("depth", usize::MAX, self.limits.max_depth()))?;
362 self.limits.check_depth(container_depth)?;
363 let start = self.position;
364 self.position += 1;
365 let mut entries = Vec::new();
366 loop {
367 match self.input.get(self.position) {
368 Some(b'e') => {
369 self.position += 1;
370 break;
371 }
372 Some(b'0'..=b'9') => {
373 self.budget
374 .charge(std::mem::size_of::<(ByteString<'input>, Value<'input>)>())?;
375 entries.try_reserve_exact(1).map_err(|_| {
376 Error::resource_limit(
377 "owned allocation",
378 usize::MAX,
379 self.limits.max_owned_allocation(),
380 )
381 })?;
382 let key = self.parse_byte_string(true)?;
383 if self.input.get(self.position).is_none() {
384 return Err(Error::bencode_syntax(start, "dictionary key has no value"));
385 }
386 let value = self.parse_value(container_depth)?;
387 entries.push((key, value));
388 }
389 Some(_) => {
390 return Err(Error::bencode_syntax(
391 self.position,
392 "dictionary key must be a byte string",
393 ));
394 }
395 None => return Err(Error::bencode_syntax(start, "unterminated dictionary")),
396 }
397 }
398 Ok(Value {
399 kind: ValueKind::Dictionary(entries),
400 span: Span::new(start, self.position),
401 })
402 }
403
404 fn count_item(&mut self) -> Result<()> {
405 self.items = self.items.checked_add(1).ok_or_else(|| {
406 Error::resource_limit("item count", usize::MAX, self.limits.max_items())
407 })?;
408 self.limits.check_items(self.items)
409 }
410
411 fn peek(&self) -> Result<u8> {
412 self.input
413 .get(self.position)
414 .copied()
415 .ok_or_else(|| Error::bencode_syntax(self.position, "expected a value"))
416 }
417}
418
419#[derive(Clone, Debug, Eq, PartialEq)]
421pub enum OwnedValue {
422 Integer(i64),
424 IntegerBytes(Vec<u8>),
426 Bytes(Vec<u8>),
428 List(Vec<Self>),
430 Dictionary(std::collections::BTreeMap<Vec<u8>, Self>),
432}
433
434impl OwnedValue {
435 #[must_use]
437 pub const fn integer(value: i64) -> Self {
438 Self::Integer(value)
439 }
440
441 pub fn integer_bytes(value: impl AsRef<[u8]>) -> Result<Self> {
448 let value = value.as_ref();
449 let digits = value.strip_prefix(b"-").unwrap_or(value);
450 let valid = !digits.is_empty() && digits.iter().all(u8::is_ascii_digit);
451 let canonical_zero = digits == b"0" && !value.starts_with(b"-");
452 let canonical_nonzero = digits.first().is_some_and(|digit| *digit != b'0');
453 if value.starts_with(b"+") || !valid || !(canonical_zero || canonical_nonzero) {
454 return Err(Error::bencode_canonical(0, "invalid canonical integer"));
455 }
456 Ok(Self::IntegerBytes(value.to_vec()))
457 }
458
459 #[must_use]
461 pub fn bytes(value: impl Into<Vec<u8>>) -> Self {
462 Self::Bytes(value.into())
463 }
464
465 #[must_use]
467 pub fn list(values: impl IntoIterator<Item = Self>) -> Self {
468 Self::List(values.into_iter().collect())
469 }
470
471 pub fn dictionary(entries: impl IntoIterator<Item = (Vec<u8>, Self)>) -> Result<Self> {
477 let mut dictionary = std::collections::BTreeMap::new();
478 for (key, value) in entries {
479 if dictionary.insert(key, value).is_some() {
480 return Err(Error::bencode_canonical(0, "duplicate dictionary key"));
481 }
482 }
483 Ok(Self::Dictionary(dictionary))
484 }
485
486 #[must_use]
488 pub fn encoded_len(&self) -> usize {
489 match self {
490 Self::Integer(value) => 2 + decimal_len_i64(*value),
491 Self::IntegerBytes(value) => 2 + value.len(),
492 Self::Bytes(bytes) => decimal_len_usize(bytes.len()) + 1 + bytes.len(),
493 Self::List(values) => 2 + values.iter().map(Self::encoded_len).sum::<usize>(),
494 Self::Dictionary(entries) => {
495 2 + entries
496 .iter()
497 .map(|(key, value)| {
498 decimal_len_usize(key.len()) + 1 + key.len() + value.encoded_len()
499 })
500 .sum::<usize>()
501 }
502 }
503 }
504
505 pub fn write_to(&self, writer: &mut impl std::io::Write) -> Result<()> {
511 self.validate_integer_bytes()?;
512 self.write_canonical(writer)
513 .map_err(|source| Error::io("<writer>", source))
514 }
515
516 pub fn to_vec(&self) -> Result<Vec<u8>> {
522 self.validate_integer_bytes()?;
523 let mut encoded = Vec::with_capacity(self.encoded_len());
524 self.write_canonical(&mut encoded)
525 .map_err(|source| Error::io("<writer>", source))?;
526 Ok(encoded)
527 }
528
529 fn write_canonical(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
530 match self {
531 Self::Integer(value) => write!(writer, "i{value}e"),
532 Self::IntegerBytes(value) => {
533 writer.write_all(b"i")?;
534 writer.write_all(value)?;
535 writer.write_all(b"e")
536 }
537 Self::Bytes(bytes) => {
538 write!(writer, "{}:", bytes.len())?;
539 writer.write_all(bytes)
540 }
541 Self::List(values) => {
542 writer.write_all(b"l")?;
543 for value in values {
544 value.write_canonical(writer)?;
545 }
546 writer.write_all(b"e")
547 }
548 Self::Dictionary(entries) => {
549 writer.write_all(b"d")?;
550 for (key, value) in entries {
551 write!(writer, "{}:", key.len())?;
552 writer.write_all(key)?;
553 value.write_canonical(writer)?;
554 }
555 writer.write_all(b"e")
556 }
557 }
558 }
559
560 fn validate_integer_bytes(&self) -> Result<()> {
561 match self {
562 Self::IntegerBytes(value) => Self::integer_bytes(value).map(|_| ()),
563 Self::List(values) => values.iter().try_for_each(Self::validate_integer_bytes),
564 Self::Dictionary(entries) => {
565 entries.values().try_for_each(Self::validate_integer_bytes)
566 }
567 Self::Integer(_) | Self::Bytes(_) => Ok(()),
568 }
569 }
570}
571
572pub fn validate_canonical(input: &[u8]) -> Result<()> {
580 let value = parse(input)?;
581 validate_value_canonical(input, &value)
582}
583
584pub(crate) fn validate_parsed_canonical(input: &[u8], value: &Value<'_>) -> Result<()> {
585 validate_value_canonical(input, value)
586}
587
588fn validate_value_canonical(input: &[u8], value: &Value<'_>) -> Result<()> {
589 match value.kind() {
590 ValueKind::Integer(_) => validate_integer_encoding(input, value.span()),
591 ValueKind::Bytes(_) => validate_byte_string_encoding(input, value.span()),
592 ValueKind::List(values) => {
593 for value in values {
594 validate_value_canonical(input, value)?;
595 }
596 Ok(())
597 }
598 ValueKind::Dictionary(entries) => {
599 let mut previous: Option<&[u8]> = None;
600 for (key, value) in entries {
601 validate_byte_string_encoding(input, key.span())?;
602 if let Some(previous) = previous {
603 match key.bytes().cmp(previous) {
604 std::cmp::Ordering::Less => {
605 return Err(Error::bencode_canonical(
606 key.span().start(),
607 "dictionary keys are not sorted",
608 ));
609 }
610 std::cmp::Ordering::Equal => {
611 return Err(Error::bencode_canonical(
612 key.span().start(),
613 "duplicate dictionary key",
614 ));
615 }
616 std::cmp::Ordering::Greater => {}
617 }
618 }
619 previous = Some(key.bytes());
620 validate_value_canonical(input, value)?;
621 }
622 Ok(())
623 }
624 }
625}
626
627fn validate_integer_encoding(input: &[u8], span: Span) -> Result<()> {
628 let encoded = &input[span.start() + 1..span.end() - 1];
629 if encoded == b"-0" {
630 return Err(Error::bencode_canonical(
631 span.start() + 1,
632 "negative zero is not canonical",
633 ));
634 }
635 let digits = encoded.strip_prefix(b"-").unwrap_or(encoded);
636 if digits.len() > 1 && digits[0] == b'0' {
637 return Err(Error::bencode_canonical(
638 span.start() + 1,
639 "integer has a leading zero",
640 ));
641 }
642 Ok(())
643}
644
645fn validate_byte_string_encoding(input: &[u8], span: Span) -> Result<()> {
646 let encoded = &input[span.start()..span.end()];
647 let colon = encoded
648 .iter()
649 .position(|byte| *byte == b':')
650 .expect("parsed byte strings always contain a colon");
651 if colon > 1 && encoded[0] == b'0' {
652 return Err(Error::bencode_canonical(
653 span.start(),
654 "byte-string length has a leading zero",
655 ));
656 }
657 Ok(())
658}
659
660fn decimal_len_usize(value: usize) -> usize {
661 value.checked_ilog10().map_or(1, |log| log as usize + 1)
662}
663
664fn decimal_len_i64(value: i64) -> usize {
665 if value < 0 {
666 1 + decimal_len_u64(value.unsigned_abs())
667 } else {
668 decimal_len_u64(value.unsigned_abs())
669 }
670}
671
672fn decimal_len_u64(value: u64) -> usize {
673 value.checked_ilog10().map_or(1, |log| log as usize + 1)
674}