bencode_decoder/
lib.rs

1//! A module, providing way to parse Bencoded files (e.g. torrent files).
2//!
3//! Contains all types of elements (`BNumber`, `BList`, `BDictionary`, `BString`), which
4//! have trait `BElement` with decoding functionality.
5
6/// Trait for all bencode elements.
7///
8/// Provides way to decode some type of element `T`, which must have trait `BElement`, from
9/// array of bytes
10///
11/// # Examples
12///
13/// Simple implementation.
14///
15/// ```rust
16/// use bencode_decoder::BElement;
17///
18/// struct BExample {
19///     e: i8,
20/// }
21///
22/// impl BElement<BExample> for BExample {
23///     fn decode(encoded: &[u8]) -> Result<(usize, BExample), &'static str> {
24///         Err("No implementation, sorry")
25///     }
26/// }
27/// ```
28pub trait BElement<T> where T: BElement<T> {
29    /// Decodes element from array of bytes.
30    /// 
31    /// Returns `Ok((position of last used byte in array of bytes, parsed element))` or
32    /// `Err` if parse has failed.
33    fn decode(encoded: &[u8]) -> Result<(usize, T), &'static str>;
34}
35
36/// Struct for representing numbers in Bencode format.
37#[derive(Eq, PartialEq, Debug)]
38pub struct BNumber {
39    /// Real number, represented by `BNumber`.
40    number: i64,
41}
42
43impl BNumber {
44    /// Simple constructor from one `i64`.
45    pub fn new(number: i64) -> BNumber {
46        BNumber { number: number }
47    }
48}
49
50impl BElement<BNumber> for BNumber {
51
52    /// Decodes `BNumber` from array of bytes. 
53    /// 
54    /// Returnes `Ok((position of last used byte in passed array, parsed BNumber))` 
55    /// or `Err` if couldn't parse BNumber correctly.
56    /// # Examples
57    ///
58    /// BNumber must begin with 'i' char and end with 'e' char.
59    ///
60    /// ```rust
61    /// use bencode_decoder::BElement;
62    /// use bencode_decoder::BNumber;
63    /// assert_eq!((4, BNumber::new(300)),
64    ///                BNumber::decode("i300e".as_bytes()).ok().expect("invalid"));
65    /// assert_eq!((5, BNumber::new(-204)),
66    ///                BNumber::decode("i-204e".as_bytes()).ok().expect("invalid"));
67    /// ```
68    ///
69    /// If it's not, then error is generated.
70    ///
71    /// ```rust
72    /// use bencode_decoder::BElement;
73    /// use bencode_decoder::BNumber;
74    /// assert!(BNumber::decode("l300e".as_bytes()).is_err());
75    /// ```
76    /// 
77    /// Also error is generated, when number isn't valid or is too big for `i64`.
78    /// 
79    /// ```rust
80    /// use bencode_decoder::BElement;
81    /// use bencode_decoder::BNumber;
82    /// assert!(BNumber::decode("i400000000000000000000000000000000000000000000e".as_bytes()).is_err());
83    /// ```
84    fn decode(encoded: &[u8]) -> Result<(usize, BNumber), &'static str> {
85        if encoded.len() < 1 {
86            Err("empty string isn't valid BNumber")
87        } else {
88            match encoded[0] as char {
89                'i' => {
90                    let mut i : usize = 1;
91                    while i < encoded.len() && encoded[i] as char != 'e' {
92                        i += 1;
93                    }
94                    if i == encoded.len() {
95                        return Err("expected 'e' after number");
96                    }
97                    let number: &[u8] = &encoded[1..i];
98                    let str_number: String = String::from_utf8_lossy(number).into_owned();
99                    if let Ok(x) = str_number.parse::<i64>() {
100                        Ok((i, BNumber::new(x)))
101                    } else {
102                        Err("expected correct i64 number")
103                    }
104                },
105                _ => Err("expected 'i' before number")
106            }
107        }
108    }
109}
110
111/// Struct for representing string (byte sequence) in Bencode format.
112#[derive(Eq, PartialEq, Debug)]
113pub struct BString {
114    /// Sequence of bytes, contained in this `BString`.
115    data: String,
116}
117
118impl BString {
119    /// Simple constructor from array of bytes.
120    pub fn new(data: &[u8]) -> BString {
121        BString { data: String::from_utf8_lossy(data).into_owned() }
122    }
123}
124
125impl BElement<BString> for BString {
126    /// Decodes `BString` from array of bytes.
127    /// 
128    /// Returns `Ok((position of last used byte in passed array, parsed BString))`
129    /// or `Err` if couldn't parse `BString` correctly.
130    /// # Examples
131    /// 
132    /// `BString` must have following structure: <length>:<data>, where data - sequence
133    /// of bytes with corresponding length.
134    ///
135    /// ```
136    /// use bencode_decoder::BElement;
137    /// use bencode_decoder::BString;
138    ///
139    /// assert_eq!((4, BString::new("abc".as_bytes())), 
140    ///            BString::decode("3:abc".as_bytes()).ok().expect("invalid"));
141    /// ```
142    fn decode(encoded: &[u8]) -> Result<(usize, BString), &'static str> {
143        let mut i: usize = 0;
144        while i < encoded.len() && encoded[i] as char != ':' {
145            i += 1;
146        }
147        if i == encoded.len() {
148            Err("expected :, but end was found")
149        } else {
150            let length: &[u8] = &encoded[0..i];
151            if let Ok(x) = String::from_utf8_lossy(length).into_owned().parse::<usize>() {
152                if i + x + 1 <= encoded.len() {
153                    let value: &[u8] = &encoded[i + 1..i + x + 1];
154                    Ok((i + x, BString::new(value)))
155                } else {
156                    Err("expected more bytes, but end was found")
157                }
158            } else {
159                Err("expected correct usize number, representing length")
160            }
161        }
162    }
163}
164/*
165struct BDictionary {
166    data: HashMap<&str, [u8]>,
167}
168
169/// Basic equivalence relation.
170///
171/// Checks for equality simply using `BString`'s `data` field. Works exactly
172/// like equivalence in &[u8].
173impl PartialEq for BDictionary{
174    fn eq(&self, other: &Self) -> bool {
175        self.data == other.data
176    }
177
178    fn ne(&self, other: &Self) -> bool {
179        self.data != other.data
180    }
181}
182
183/// Guarantees to be reflexive.
184impl Eq for BDictionary {
185    
186}
187
188/// Simple `Debug` implementation.
189///
190/// Works just like `[u8]::fmt`.
191impl Debug for BDictionary {
192    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
193        self.data.fmt(f)
194    }
195}
196
197impl BDictionary {
198    /// Simple constructor from array of bytes.
199    pub fn new(data: &HashMap<&str, &str>) -> BString {
200        BString { data: data }
201    }
202}*/
203
204/// Simple test module.
205#[cfg(test)]
206mod bnumber_tests {
207    extern crate rand;
208
209    use super::*;
210
211    fn test_bnumber(string: &[u8], index: usize, result: i64) {
212        let (ind, num) = BNumber::decode(string).ok().expect("invalid test");
213        assert_eq!(result, num.number);
214        assert_eq!(index, ind);
215    }
216
217    fn test_bnumber_invalid(string: &[u8], expected: &str) {
218        let error = BNumber::decode(string).err().expect("invalid test");
219        assert_eq!(expected, error);
220    }
221
222    #[test]
223    fn test1_bnumber_simple() {
224        test_bnumber("i300e".as_bytes(), 4, 300);
225    }
226
227    #[test]
228    fn test2_bnumber_negative() {
229        test_bnumber("i-23487e".as_bytes(), 7, -23487);
230    }
231
232    #[test]
233    fn test3_bnumber_invalid_format() {
234        test_bnumber_invalid("l487e".as_bytes(), "expected 'i' before number");
235    }
236
237    #[test]
238    fn test4_bnumber_invalid_format() {
239        test_bnumber_invalid("i487k".as_bytes(), "expected 'e' after number");
240    }
241
242    #[test]
243    fn test5_bnumber_invalid_number() {
244        test_bnumber_invalid("i-650-4e".as_bytes(), "expected correct i64 number");
245    }
246
247    #[test]
248    fn test6_bnumber_too_big_number() {
249        test_bnumber_invalid("i2398475629384765298346529384756293487562923452983e".as_bytes()
250                             , "expected correct i64 number");
251    }
252
253    #[test]
254    fn test7_bnumber_zero() {
255        test_bnumber("i0e".as_bytes(), 2, 0);
256    }
257
258    #[test]
259    fn test8_bnumer_empty_number() {
260        test_bnumber_invalid("ie".as_bytes(), "expected correct i64 number");
261    }
262
263    #[test]
264    fn test9_bnumer_too_short() {
265        test_bnumber_invalid("i".as_bytes(), "expected 'e' after number");
266    }
267
268    #[test]
269    fn test10_bnumer_even_shorter() {
270        test_bnumber_invalid("".as_bytes(), "empty string isn't valid BNumber");
271    }
272
273    #[test]
274    fn test11_bnumer_stress() {
275        for _ in 0..100000 {
276            let number: i64 = rand::random::<i64>();
277            let string: String = format!("i{}e", number);
278            test_bnumber(string.as_bytes(), string.len() - 1, number);
279        }
280    }
281}
282
283#[cfg(test)]
284mod bstring_tests {
285    use super::*;
286    fn test_bstring(string: &[u8], index: usize, result: String) {
287        let (ind, bstr) = BString::decode(string).ok().expect("invalid test");
288        assert_eq!(index, ind);
289        assert_eq!(result, bstr.data);
290    }
291
292    fn test_bstring_invalid(string: &[u8], expected: &str) {
293        let error = BString::decode(string).err().expect("invalid test");
294        assert_eq!(expected, error);
295    }
296
297    #[test]
298    fn test1_bstring_simple() {
299        test_bstring("3:abc".as_bytes(), 4, "abc".to_string());
300    }
301
302    #[test]
303    fn test2_bstring_short() {
304        test_bstring("1:a".as_bytes(), 2, "a".to_string());
305    }
306
307    #[test]
308    fn test3_bstring_even_shorter() {
309        test_bstring("0:".as_bytes(), 1, "".to_string());
310    }
311
312    #[test]
313    fn test4_bstring_digits() {
314        test_bstring("5:12345".as_bytes(), 6, "12345".to_string());
315    }
316
317    #[test]
318    fn test5_bstring_bad_symbols() {
319        test_bstring("14:!@#$%^&*()_+-=".as_bytes(), 16, "!@#$%^&*()_+-=".to_string());
320    }
321
322    #[test]
323    fn test6_bstring_empty() {
324        test_bstring_invalid("".as_bytes(), "expected :, but end was found");
325    }
326
327    #[test]
328    fn test7_bstring_bad_len() {
329        test_bstring_invalid("1:".as_bytes(), "expected more bytes, but end was found");
330    }
331
332    #[test]
333    fn test8_bstring_no_colon() {
334        test_bstring_invalid("128911".as_bytes(), "expected :, but end was found");
335    }
336
337    #[test]
338    fn test9_bstring_invalid_len() {
339        test_bstring_invalid("2a:a".as_bytes(), "expected correct usize number, representing length");
340    }
341
342    #[test]
343    fn test10_bstring_colon_first() {
344        test_bstring_invalid(":123".as_bytes(), "expected correct usize number, representing length");
345    }
346}