bendy 0.6.0

A rust library for encoding and decoding bencode with enforced canonicalization rules.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
# Bendy

[![Build Status](https://travis-ci.org/P3KI/bendy.svg?branch=master)](https://travis-ci.org/P3KI/bendy)
[![Current Version](https://meritbadge.herokuapp.com/bendy)](https://crates.io/crates/bendy)
[![License: BSD-3-Clause](https://img.shields.io/github/license/P3KI/bendy.svg)](https://github.com/P3KI/bendy/blob/master/LICENSE-BSD3)

A Rust library for encoding and decoding bencode with enforced canonicalization rules.
It also provides some facilities for reflecting over arbitrary decoded bencode structures.
[Bencode](https://en.wikipedia.org/wiki/Bencode) is a simple but very effective encoding
scheme, originating with the BitTorrent peer-to-peer system.

---

You may be looking for:

- [Known Alternatives]#known-alternatives
- [Why should I use it]#why-should-i-use-it
- [Usage]#usage
 - [Feature Flags]#feature-flags
 - [Encoding]#encoding-with-tobencode
 - [Decoding]#decoding-with-frombencode
 - [Serde Support]#serde-support
 - [Reflection]#reflection
- [Unsafe Code]#usage-of-unsafe-code
- [Contributing]#contributing

---

## Known alternatives:
This is not the first library to implement Bencode. In fact there are several
implementations already:

- Toby Padilla [serde-bencode]https://github.com/toby/serde-bencode
- Arjan Topolovec's [rust-bencode]https://github.com/arjantop/rust-bencode,
- Murarth's [bencode]https://github.com/murarth/bencode,
- and Jonas Hermsmeier's [rust-bencode]https://github.com/jhermsmeier/rust-bencode

## Why should I use it?
So why the extra work adding yet-another-version of a thing that already exists, you
might ask?

### Enforced correctness
Implementing a canonical encoding form is straight forward. It comes down to defining
*a proper way of handling unordered data*. The next step is that bendy's sorting data
before encoding it using the regular Bencode rules. If your data is already sorted bendy
will of course skip the extra sorting step to gain efficiency.
But bendy goes a step further to *ensure correctness*: If you hand the library data that
you say is already sorted, bendy still does an in-place verification to *ensure that your
data actually is sorted* and complains if it isn't. In the end, once bendy serialized
your data, it's Bencode through and through. So it's perfectly compatible with every
other Bencode library.

Just remember: At this point *only bendy* enforces the correctness of the canonical
format if you read it back in.

### Canonical representation
Bendy ensures that any de-serialize / serialize round trip produces the exact *same*
and *correct* binary representation. This is relevant if you're dealing with unordered
sets or map-structured data where theoretically the order is not relevant, but in
practice it is, especially if you want to ensure that cryptographic signatures related
to the data structure do not get invalidated accidentally.

| Data Structure | Default Impl | Comment                                                                                    |
|----------------|--------------|--------------------------------------------------------------------------------------------|
| Vec            || Defines own ordering                                                                       |
| VecDeque       || Defines own ordering                                                                       |
| LinkedList     || Defines own ordering                                                                       |
| HashMap        || Ordering missing but content is ordered by key byte representation.                        |
| BTreeMap       || Defines own ordering                                                                       |
| HashSet        || (Unordered) Set handling not yet defined                                                   |
| BTreeSet       || (Unordered) Set handling not yet defined                                                   |
| BinaryHeap     || Ordering missing                                                                           |
| Iterator       | ~            | `emit_unchecked_list()` allows to emit any iterable but user needs to ensure the ordering. |

**Attention:**

- Since most list types already define their inner ordering, data structures
  like `Vec`, `VecDeque`, and `LinkedList` will not get sorted during encoding!

- There is no default implementation for handling generic iterators.
  This is by design. `Bendy` cannot tell from an iterator whether the underlying
  structure requires sorting or not and would have to take data as-is.

## Usage

First you need to add bendy as a project dependency:

```toml
[dependencies]
bendy = "^0.4"
```

### Feature flags

Bendy has the following feature flags:
* `std` - Enabled by default.
* `serde` - Support serde. Requires `std` to be enabled.
* `inspect` - Include reflection facilities.

### Encoding with `ToBencode`

To encode an object of a type which already implements the `ToBencode` trait
it is enough to import the trait and call the `to_bencode()` function on the object.

```rust
use bendy::encoding::{ToBencode, Error};

let my_data = vec!["hello", "world"];
let encoded = my_data.to_bencode()?;

assert_eq!(b"l5:hello5:worlde", encoded.as_slice());

Ok::<(), Error>(())
```

### Implementing `ToBencode`

In most cases it should be enough to overwrite the associated `encode` function
and keep the default implementation of `to_bencode`.

The function will provide you with a `SingleItemEncoder` which must be used to
emit any relevant components of the current object. As long as these implement
`ToBencode` themselves it is enough to pass them into the `emit` function of
the encoder as this will serialize any type implementing the trait.

Next to `emit` the encoder also provides a list of functions to encode specific
bencode primitives (i.e. `emit_int` and `emit_str`) and nested bencode elements
(i.e. `emit_dict` and `emit_list`). These methods should be used if its necessary
to output a specific non default data type.

**Implementing Integer Encoding**

As bencode has native integer support bendy provides default implementations for
all of rusts native integer types. This allows to call `to_bencode` on any integer
object and to pass these objects into the encoder's `emit_int` function.

```rust
use bendy::encoding::{ToBencode, SingleItemEncoder, Error};

struct IntegerWrapper(i64);

impl ToBencode for IntegerWrapper {
    const MAX_DEPTH: usize = 0;

    fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> {
        encoder.emit_int(self.0)
    }
}

let example = IntegerWrapper(21);

let encoded = example.to_bencode()?;
assert_eq!(b"i21e", encoded.as_slice());

let encoded = 21.to_bencode()?;
assert_eq!(b"i21e", encoded.as_slice());

Ok::<(), Error>(())
```

**Encode a byte string**

Another data type bencode natively supports are byte strings. Therefore bendy
provides default implementations for `String` and `&str`.

```rust
use bendy::encoding::{ToBencode, SingleItemEncoder, Error};

struct StringWrapper(String);

impl ToBencode for StringWrapper {
    const MAX_DEPTH: usize = 0;

    fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> {
        encoder.emit_str(&self.0)
    }
}

let example = StringWrapper("content".to_string());

let encoded = example.to_bencode()?;
assert_eq!(b"7:content", encoded.as_slice());

let encoded = "content".to_bencode()?;
assert_eq!(b"7:content", encoded.as_slice());

Ok::<(), Error>(())
```

As its a very common pattern to represent a byte string as `Vec<u8>` bendy
exposes the `AsString` wrapper. This can be used to encapsulate any element
implementing `AsRef<[u8]>` to output itself as a bencode string instead of a
list.

```rust
use bendy::encoding::{ToBencode, SingleItemEncoder, Error, AsString};

struct ByteStringWrapper(Vec<u8>);

impl ToBencode for ByteStringWrapper {
    const MAX_DEPTH: usize = 0;

    fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> {
        let content = AsString(&self.0);
        encoder.emit(&content)
    }
}

let example = ByteStringWrapper(b"content".to_vec());

let encoded = example.to_bencode()?;
assert_eq!(b"7:content", encoded.as_slice());

let encoded = AsString(b"content").to_bencode()?;
assert_eq!(b"7:content", encoded.as_slice());

Ok::<(), Error>(())
```

**Encode a dictionary**

If a data structure contains key-value pairs its most likely a good idea to
encode it as a bencode dictionary. This is also true for most structs with
more then one member as it might be helpful to represent their names to ensure
the existence of specific (optional) member.

__Attention:__ To ensure a canonical representation bendy requires that the keys
of a dictionary emitted via `emit_dict` are sorted in ascending order or the
encoding will fail with an error of kind `UnsortedKeys`. In case of an unsorted
dictionary it might be useful to use `emit_and_sort_dict` instead.

```rust
use bendy::encoding::{ToBencode, SingleItemEncoder, Error};

struct Example {
    label: String,
    counter: u64,
}

impl ToBencode for Example {
    const MAX_DEPTH: usize = 1;

    fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> {
        encoder.emit_dict(|mut e| {
            e.emit_pair(b"counter", &self.counter)?;
            e.emit_pair(b"label", &self.label)?;

            Ok(())
        })
    }
}

let example = Example { label: "Example".to_string(), counter: 0 };

let encoded = example.to_bencode()?;
assert_eq!(b"d7:counteri0e5:label7:Examplee", encoded.as_slice());

Ok::<(), Error>(())
```

**Encode a list**

While encoding a list bendy assumes the elements inside this list are
inherently sorted through their position inside the list. The implementation
is therefore free to choose its own sorting.

```rust
use bendy::encoding::{ToBencode, SingleItemEncoder, Error};

struct Location(i64, i64);

impl ToBencode for Location {
    const MAX_DEPTH: usize = 1;

    fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> {
        encoder.emit_list(|e| {
            e.emit_int(self.0)?;
            e.emit_int(self.1)
        })
    }
}

let example = Location(2, 3);

let encoded = example.to_bencode()?;
assert_eq!(b"li2ei3ee", encoded.as_slice());

Ok::<(), Error>(())
```

### Decoding with `FromBencode`

To decode an object of a type which already implements the `FromBencode` trait
it is enough to import the trait and call the `from_bencode()` function on the object.

```rust
use bendy::decoding::{FromBencode, Error};

let encoded = b"l5:hello5:worlde".to_vec();
let decoded = Vec::<String>::from_bencode(&encoded)?;

assert_eq!(vec!["hello", "world"], decoded);

Ok::<(), Error>(())
```

### Implementing `FromBencode`

In most cases it should be enough to overwrite the associated
`decode_bencode_object` function and keep the default implementation of
`from_bencode`.

The function will provide you with an representation of a bencode `Object`
which must be processed to receive any relevant components of the expected data
type. As long as these implement `FromBencode` themselves it is enough to call
`decode_bencode_object` on the expected data type of the element as this will
deserialize any type implementing the trait.

Next to `from_bencode` the bencode `Object` representation also provides a list
of helper functions to itself into specific bencode primitives and container
(i.e. `bytes_or`, `integer_or_else` or `try_into_list`). Which than can be used
to restore the actual element.

**Decode an integer**

As bencode has native integer support bendy provides default implementations
for all of rusts native integer types. This allows to call `from_bencode` on
any type of integer.

*Attention:* If it's necessary to handle a big integer which has no
representation through one of the default data types it's always possible to
access the string version of the number during decoding.

```rust
use bendy::decoding::{FromBencode, Object, Error};

#[derive(Debug, Eq, PartialEq)]
struct IntegerWrapper(i64);

impl FromBencode for IntegerWrapper {
    const EXPECTED_RECURSION_DEPTH: usize = 0;

    fn decode_bencode_object(object: Object) -> Result<Self, Error> {
        // This is an example for content handling. It would also be possible
        // to call  `i64::decode_bencode_object(object)` directly.
        let content = object.try_into_integer()?;
        let number = content.parse::<i64>()?;

        Ok(IntegerWrapper(number))
    }
}

let encoded = b"i21e".to_vec();

let example = IntegerWrapper::from_bencode(&encoded)?;
assert_eq!(IntegerWrapper(21), example);

let example = i64::from_bencode(&encoded)?;
assert_eq!(21, example);

Ok::<(), Error>(())
```

**Decode a byte string**

In most cases it is possible to restore a string from its bencode
representation as a byte sequence via the `String::from_utf8` and
`str::from_utf8`.

```rust
use bendy::decoding::{FromBencode, Object, Error};

#[derive(Debug, Eq, PartialEq)]
struct StringWrapper(String);

impl FromBencode for StringWrapper {
    const EXPECTED_RECURSION_DEPTH: usize = 0;

    fn decode_bencode_object(object: Object) -> Result<Self, Error> {
        // This is an example for content handling. It would also be possible
        // to call  `String::decode_bencode_object(object)` directly.
        let content = object.try_into_bytes()?;
        let content = String::from_utf8(content.to_vec())?;

        Ok(StringWrapper(content))
    }
}

let encoded = b"7:content".to_vec();

let example = StringWrapper::from_bencode(&encoded)?;
assert_eq!(StringWrapper("content".to_string()), example);

let example = String::from_bencode(&encoded)?;
assert_eq!("content".to_string(), example);

Ok::<(), Error>(())
```

If the content is a non utf8 encoded string or an actual byte sequence the
`AsString` wrapper might be useful to restore the bencode string object as
a sequence of bytes through an object of type `Vec<u8>`.

```rust
use bendy::{
    decoding::{FromBencode, Object, Error},
    encoding::AsString,
};

#[derive(Debug, Eq, PartialEq)]
struct ByteStringWrapper(Vec<u8>);

impl FromBencode for ByteStringWrapper {
    const EXPECTED_RECURSION_DEPTH: usize = 0;

    fn decode_bencode_object(object: Object) -> Result<Self, Error> {
        let content = AsString::decode_bencode_object(object)?;
        Ok(ByteStringWrapper(content.0))
    }
}

let encoded = b"7:content".to_vec();

let example = ByteStringWrapper::from_bencode(&encoded)?;
assert_eq!(ByteStringWrapper(b"content".to_vec()), example);

let example = AsString::from_bencode(&encoded)?;
assert_eq!(b"content".to_vec(), example.0);

Ok::<(), Error>(())
```

**Decode a dictionary**

Unwrapping the bencode object into a dictionary will provide a dictionary
decoder which can be used to access the included key-value pairs.

To improve the error handling in case of huge or multiple nested dictionaries
the decoding module provides a `ResultExt` trait which allows to add a context
description in case of an error. If multiple context calls are nested they will
concatenated in a dot notation like style.

```rust
use bendy::decoding::{FromBencode, Object, Error, ResultExt};

#[derive(Debug, Eq, PartialEq)]
struct Example {
    label: String,
    counter: u64,
}

impl FromBencode for Example {
    const EXPECTED_RECURSION_DEPTH: usize = 1;

    fn decode_bencode_object(object: Object) -> Result<Self, Error> {
        let mut counter = None;
        let mut label = None;

        let mut dict = object.try_into_dictionary()?;
        while let Some(pair) = dict.next_pair()? {
            match pair {
                (b"counter", value) => {
                    counter = u64::decode_bencode_object(value)
                        .context("counter")
                        .map(Some)?;
                },
                (b"label", value) => {
                    label = String::decode_bencode_object(value)
                        .context("label")
                        .map(Some)?;
                },
                (unknown_field, _) => {
                    return Err(Error::unexpected_field(String::from_utf8_lossy(
                        unknown_field,
                    )));
                },
            }
        }

        let counter = counter.ok_or_else(|| Error::missing_field("counter"))?;
        let label= label.ok_or_else(|| Error::missing_field("label"))?;

        Ok(Example { counter, label })
    }
}

let encoded = b"d7:counteri0e5:label7:Examplee".to_vec();
let expected = Example { label: "Example".to_string(), counter: 0 };

let example = Example::from_bencode(&encoded)?;
assert_eq!(expected, example);

Ok::<(), Error>(())
```

**Decode a list**

Unwrapping the bencode object into a list will provide a list decoder which can
be used to access the contained elements.

```rust
use bendy::decoding::{FromBencode, Object, Error};

#[derive(Debug, PartialEq, Eq)]
struct Location(i64, i64);

impl FromBencode for Location {
    const EXPECTED_RECURSION_DEPTH: usize = 1;

    fn decode_bencode_object(object: Object) -> Result<Self, Error> {
        let mut list = object.try_into_list()?;

        let x = list.next_object()?.ok_or(Error::missing_field("x"))?;
        let x = i64::decode_bencode_object(x)?;

        let y = list.next_object()?.ok_or(Error::missing_field("y"))?;
        let y = i64::decode_bencode_object(y)?;

        Ok(Location(x, y))
    }
}

let encoded = b"li2ei3ee".to_vec();
let expected = Location(2, 3);

let example = Location::from_bencode(&encoded)?;
assert_eq!(expected, example);

Ok::<(), Error>(())
```

### Optional: Limitation of recursive parsing

**What?**

The library allows to set an expected recursion depth limit for de- and encoding.
If set, the parser will use this value as an upper limit for the validation of any nested
data structure and abort with an error if an additional level of nesting is detected.

While the encoding limit itself is primarily there to increase the confidence of bendy
users in their own validation code, the decoding limit should be used to avoid
parsing of malformed or malicious external data.

 - The encoding limit can be set through the `MAX_DEPTH` constant in any implementation
   of the `ToBencode` trait.
 - The decoding limit can be set through the `EXPECTED_RECURSION_DEPTH` constant in any
   implementation of the `FromBencode` trait.

**How?**

The nesting level calculation always starts on level zero, is incremented by one when
the parser enters a nested bencode element (i.e. list, dictionary) and decrement as
soon as the related element ends. Therefore any values decoded as bencode strings
or integers do not affect the nesting limit.

### Serde Support

Bendy supports serde when the `serde` feature is enabled:

```toml
[dependencies]
bendy = { version = "^0.3", features = ["std", "serde"] }
serde = { version = "1.0", features = ["derive"] }
```

With the feature enabled, values can be serialized to and deserialized from
bencode with `bendy::serde::from_bytes` and `bendy::serde::to_bytes`
respectively:


```rust
# #[cfg(not(feature = "serde"))]
# fn main() {}
# #[cfg(feature = "serde")]
# fn main() -> Result<(), bendy::serde::Error> {

use serde_derive::{Deserialize, Serialize};

#[serde(crate = "serde_")]
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo {
    bar: String,
}

let value = Foo {
    bar: "hello".into(),
};

let bencode = bendy::serde::to_bytes(&value)?;
assert_eq!(bencode, b"d3:bar5:helloe");

let deserialized = bendy::serde::from_bytes::<Foo>(&bencode)?;
assert_eq!(deserialized, value);

Ok(())

# }
```

Information on how Rust types are represented in bencode is available in the
[serde module documentation](https://docs.rs/bendy/*/bendy/serde/index.html).

### Reflection

Bendy supports rudimentary reflection for use in testing, fuzzing, and debugging.

> [!WARNING]
> This feature SHOULD NOT be used in production code.
> It is not as thoroughly tested as other parts of bendy.

To enable the feature only in your tests, you can depend on bendy like so:

```toml
[dependencies.bendy]
version = "^0.4"

[dev-dependencies.bendy]
version = "^0.4"
features = ["inspect"]
```

The API has been designed for convenience. It is intended to be used to construct evil data to ensure your program responds appropriately to it. One way of doing so is:

1) Build a valid serializable object that your program should be able to handle.
1) Encode the object to bencode.
1) Ingest the resulting bytes with the `inspect::inspect` method.
1) Use the returned `Inspectable` to make modifications that break your program's invariants.
1) Call `.emit()` on the `Inspectable` to receive a `Vec` of bytes which represents your modified object.
1) Attempt to deserialize the object and ensure deserialization fails if it should.
1) Attempt to use the deserialized object and ensure your program fails in the way it ought to.

One can also use the constructor methods of `Inspectable` to manually build objects, for example
when fuzzing.

The core of the API is the `Inspectable` enum, which represents the core primitives of bencode:
numbers, bytestrings, dicts and lists. The latter two may contain other `Inspectables`,
so this forms a rough AST-like data structure. There is also an enum variant
`Inspectable::Raw` which can be used to inject arbitrary bytes into a structure.

Inspectables have methods for traversal of the AST, rudimentary search, and mutation of nodes.
Most methods will panic instead of returning `Results` or `Options`, since any broken assumption
should result in a test failure.

Here is an example of making some modifications to an AST.

```rust
# #[cfg(not(feature = "inspect"))]
# fn main() {}
# #[cfg(feature = "inspect")]
# fn main() {
use bendy::inspect::*;

// {
//   aaa: 10,
//   bbb: "hello",
//   other: [
//     {secret: "password"},
//   ],
// }
let bytes = b"\
d\
    3:aaa\
    i10e\
    3:bbb\
    5:hello\
    5:other\
    l\
        d\
            6:secret\
            8:password\
        e\
    e\
e\
";

let mut i = inspect(bytes);

println!("Pretty printed repr:\n{}", i.as_rust_string_literal());

// Access list items or dict entries with `nth`
i.dict().nth(0).value
    .replace(Inspectable::new_raw(b"invalid"));

// Dict entries can also be accessed with the `entry` method
i.dict().entry(b"bbb").value
    .set_content_byterange(0..2, b'X');

// A Path object can be used to represent traversals.
// This is often more convenient, and enables re-use.

// Above, the `.dict()` call is used to coerce from
// `&mut Inspectable` to `&mut InDict`. When traversing
// deep structures, this can get quite verbose. This is
// not needed when using Paths for traversal.

// Paths can also be used to search the AST. Searches
// can only find one occurrence of an item. The first
// occurrence in the serialized representation will be
// found (depth first search in the AST).

i.find(&inspect_path().search_entry(b"secret").value())
    .clear_content();

// Or without search:
i.find(&inspect_path().entry(b"other").value().nth(0).nth(0).value())
    .clear_content();

assert_eq!(
    i.emit().as_slice(),
    b"d3:aaainvalid3:bbb5:XXllo5:otherld6:secret0:eee"
);
# }
```

See the inspect module in the [docs](https://docs.rs/bendy/latest/bendy/) for the full API.

## Usage of unsafe code
The parser would not require any unsafe code to work but it still contains a single unsafe call
to `str::from_utf8_unchecked`. This call is used to avoid a duplicated UTF-8 check when the
parser converts the bytes representing an incoming integer into a `&str` after its successful
validation.

Known unsafe usage from dependencies as of 2025-08-14:
* serde and serde_bytes: Eight usages. Only a risk if the `serde` feature is enabled.
* smallvec: Three usages. Only a risk if the `inspect` feature is enabled.

*Disclaimer: Further unsafe code may be introduced through the dependency on the `thiserror` and `rustversion` crates. As of 2025-08-14 these contain no unsafe code.*

## Contributing

We welcome everyone to ask questions, open issues or provide merge requests.
Each merge request will be reviewed and either landed in the main tree or given
feedback for changes that would be required.

All code in this repository is under the [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause)
license.