rubit_bencode/
decode.rs

1use core::str;
2use std::{collections::HashMap, process::exit};
3
4
5use crate::{errors::ParseError, get_hash};
6
7const INTEGER_START: u8 = 0x69; // 'i'
8const STRING_DELIM: u8 = 0x3A; // ':'
9const DICTIONARY_START: u8 = 0x64; // 'd'
10const LIST_START: u8 = 0x6C; // 'l'
11const END_OF_TYPE: u8 = 0x65; // 'e'
12
13pub type Peers = Vec<((u8, u8, u8, u8), u16)>;
14
15#[derive(Debug, PartialEq)]
16pub enum BencodeTypes {
17    String(String),
18    Integer(u64),
19    List(Vec<BencodeTypes>),
20    Dict(HashMap<String, BencodeTypes>),
21    InfoHash([u8; 20]),
22    Pieces(Vec<[u8; 20]>),
23    PeersCompact(Peers),
24}
25
26fn parse_to_utf8(slice: &[u8]) -> Result<String, ParseError> {
27    let string_slice = str::from_utf8(slice)?;
28    Ok(string_slice.to_string())
29}
30
31fn parse_to_usize(slice: &[u8]) -> Result<usize, ParseError> {
32    let string = parse_to_utf8(slice)?;
33    Ok(string.parse()?)
34}
35
36fn get_string_len(pointer: &mut usize, buf: &Vec<u8>) -> Result<usize, ParseError> {
37    let mut temp = Vec::new();
38
39    // Increment and push to vec until delim
40    while buf[*pointer] != STRING_DELIM {
41        temp.push(buf[*pointer]);
42        *pointer += 1;
43    }
44
45    // Place pointer on the start of the string (after delim)
46    *pointer += 1;
47
48    if temp.len() == 1 && temp[0] == 48 {
49        return Ok(0);
50    }
51
52    Ok(parse_to_usize(&temp)?)
53}
54
55pub fn decode_string(pointer: &mut usize, buf: &Vec<u8>) -> Result<String, ParseError> {
56    let string_len = get_string_len(pointer, buf)?;
57
58    if string_len == 0 {
59        return Ok("".to_string());
60    }
61
62    let string_len = string_len + *pointer;
63    let slice: &[u8] = &buf[*pointer..string_len];
64
65    // Place pointer at the byte after the string (after the last char)
66    *pointer = string_len;
67
68    Ok(parse_to_utf8(slice)?)
69}
70
71pub fn decode_int(pointer: &mut usize, buf: &Vec<u8>) -> Result<u64, ParseError> {
72    let mut int_bytes = Vec::new();
73
74    // Place pointer at start of int (after "i")
75    *pointer += 1;
76
77    while buf[*pointer] != END_OF_TYPE {
78        int_bytes.push(buf[*pointer]);
79        *pointer += 1;
80    }
81
82    // Place pointer at end of type (after "e")
83    *pointer += 1;
84
85    Ok(parse_to_usize(&int_bytes)? as u64)
86}
87
88pub fn decode_list(pointer: &mut usize, buf: &Vec<u8>) -> Result<Vec<BencodeTypes>, ParseError> {
89    let mut list: Vec<BencodeTypes> = Vec::new();
90
91    // Place pointer at start of list (after "l")
92    *pointer += 1;
93
94    while buf[*pointer] != END_OF_TYPE {
95        list.push(match buf[*pointer] {
96            n if n.is_ascii_digit() => BencodeTypes::String(decode_string(pointer, buf)?),
97            INTEGER_START => BencodeTypes::Integer(decode_int(pointer, buf)?),
98            LIST_START => BencodeTypes::List(decode_list(pointer, buf)?),
99            DICTIONARY_START => BencodeTypes::Dict(decode_dict(pointer, buf)?),
100            _ => todo!(),
101        })
102    }
103
104    // Place pointer at end of type (after "e")
105    *pointer += 1;
106
107    Ok(list)
108}
109
110pub fn decode_pieces(pointer: &mut usize, buf: &Vec<u8>) -> Result<Vec<[u8; 20]>, ParseError> {
111    let pieces_len = get_string_len(pointer, buf)?;
112
113    let pieces_len = pieces_len + *pointer;
114
115    let pieces_vec: Vec<[u8; 20]> = buf[*pointer..pieces_len]
116        .chunks_exact(20)
117        .map(|h| match h.try_into() {
118            Ok(h) => h,
119            Err(e) => {
120                println!("bad pieces array! e: {e}");
121                exit(1);
122            }
123        })
124        .collect();
125
126    // Place pointer at the byte after the string (after the last char)
127    *pointer = pieces_len;
128
129    Ok(pieces_vec)
130}
131
132fn decode_peers(pointer: &mut usize, buf: &Vec<u8>) -> Result<BencodeTypes, ParseError> {
133    if buf[*pointer] == LIST_START {
134        let decoded = decode_list(pointer, buf)?;
135        return Ok(BencodeTypes::List(decoded));
136    }
137
138    let peers_len = get_string_len(pointer, buf)?;
139
140    let peers_len = peers_len + *pointer;
141
142    let peers_vec = buf[*pointer..peers_len]
143        .chunks_exact(6)
144        .map(|item| {
145            (
146                (item[0], item[1], item[2], item[3]),
147                u16::from_be_bytes([item[4], item[5]]),
148            )
149        })
150        .collect();
151
152    // Place pointer at the byte after the string (after the last char)
153    *pointer = peers_len;
154
155    Ok(BencodeTypes::PeersCompact(peers_vec))
156}
157
158pub fn decode_dict(
159    pointer: &mut usize,
160    buf: &Vec<u8>,
161) -> Result<HashMap<String, BencodeTypes>, ParseError> {
162    if buf.len() == 0 {
163        return Err(ParseError::BadFile);
164    }
165
166    let mut dict: HashMap<String, BencodeTypes> = HashMap::new();
167
168    // Place pointer at start of dict (after "d")
169    *pointer += 1;
170
171    let mut is_key = true;
172    let mut temp_key = String::new();
173    let mut info_hash_start: usize = 0;
174
175    while buf[*pointer] != END_OF_TYPE && *pointer != buf.len() {
176        if is_key {
177            temp_key = decode_string(pointer, buf)?;
178            if temp_key == "info" {
179                info_hash_start = *pointer;
180            }
181            is_key = !is_key;
182            continue;
183        }
184
185        let parsed = match buf[*pointer] {
186            n if n.is_ascii_digit() && temp_key == "pieces" => {
187                BencodeTypes::Pieces(decode_pieces(pointer, buf)?)
188            }
189            n if n.is_ascii_digit() && temp_key == "peers" => decode_peers(pointer, buf)?,
190            n if n.is_ascii_digit() => BencodeTypes::String(decode_string(pointer, buf)?),
191            INTEGER_START => BencodeTypes::Integer(decode_int(pointer, buf)?),
192            LIST_START => BencodeTypes::List(decode_list(pointer, buf)?),
193            DICTIONARY_START => BencodeTypes::Dict(decode_dict(pointer, buf)?),
194            _ => todo!(),
195        };
196
197        dict.insert(temp_key.clone(), parsed);
198        is_key = !is_key;
199    }
200
201    // info exists in file so we get the info_hash
202    if info_hash_start != 0 {
203        let slice = &buf[info_hash_start..*pointer];
204        let hash = get_hash(slice)?;
205        dict.insert(String::from("info_hash"), BencodeTypes::InfoHash(hash));
206    }
207
208    // Place pointer at end of type (after "e")
209    *pointer += 1;
210
211    Ok(dict)
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn decodes_string_and_advances_pointer() {
220        let test_vec = b"11:HelloWorld!".to_vec();
221        let mut pointer = 0;
222        let result = decode_string(&mut pointer, &test_vec).unwrap();
223
224        assert_eq!(String::from("HelloWorld!"), result);
225        assert_eq!(pointer, 14);
226    }
227
228    #[test]
229    fn decodes_int_and_advances_pointer() {
230        let test_vec = b"i5657e".to_vec();
231        let mut pointer = 0;
232        let result = decode_int(&mut pointer, &test_vec).unwrap();
233
234        assert_eq!(5657 as u64, result);
235        assert_eq!(pointer, 6);
236    }
237
238    #[test]
239    fn decodes_list_and_advances_pointer() {
240        let test_vec =
241            b"l11:HelloWorld!i5657el11:HelloWorld!i5657eed3:bar4:spam3:fooi42eee".to_vec();
242        let mut pointer = 0;
243        let result = decode_list(&mut pointer, &test_vec).unwrap();
244
245        let string = String::from("HelloWorld!");
246        let bar = String::from("bar");
247        let spam = String::from("spam");
248        let foo = String::from("foo");
249        let int: u64 = 5657;
250
251        let dict: HashMap<String, BencodeTypes> = HashMap::from([
252            (bar.clone(), BencodeTypes::String(spam.clone())),
253            (foo.clone(), BencodeTypes::Integer(42)),
254        ]);
255
256        assert_eq!(
257            vec![
258                BencodeTypes::String(string.clone()),
259                BencodeTypes::Integer(int.clone()),
260                BencodeTypes::List(vec![
261                    BencodeTypes::String(string.clone()),
262                    BencodeTypes::Integer(int.clone()),
263                ]),
264                BencodeTypes::Dict(dict),
265            ],
266            result,
267        );
268        assert_eq!(pointer, 66);
269    }
270
271    #[test]
272    fn decodes_dict_and_advances_pointer() {
273        let test_vec = b"d11:HelloWorld!i42e4:listll4:testel4:testeee".to_vec();
274        let mut pointer = 0;
275        let result = decode_dict(&mut pointer, &test_vec).unwrap();
276
277        let string = String::from("HelloWorld!");
278        let test = String::from("test");
279        let list = String::from("list");
280        let int: u64 = 42;
281
282        let dict: HashMap<String, BencodeTypes> = HashMap::from([
283            (string.clone(), BencodeTypes::Integer(int)),
284            (
285                list.clone(),
286                BencodeTypes::List(vec![
287                    BencodeTypes::List(vec![BencodeTypes::String(test.clone())]),
288                    BencodeTypes::List(vec![BencodeTypes::String(test.clone())]),
289                ]),
290            ),
291        ]);
292
293        assert_eq!(dict, result);
294        assert_eq!(pointer, 44);
295    }
296}