spikard_http/
query_parser.rs

1//! Fast query string parser
2//!
3//! Vendored and adapted from https://github.com/litestar-org/fast-query-parsers
4//! Original author: Naaman Hirschfeld (same author as Spikard)
5//!
6//! This parser handles multiple values for the same key and auto-converts types.
7
8use lazy_static::lazy_static;
9use regex::Regex;
10use rustc_hash::FxHashMap;
11use serde_json::{Value, from_str};
12use std::borrow::Cow;
13use std::convert::Infallible;
14
15lazy_static! {
16    static ref PARENTHESES_RE: Regex = Regex::new(r"(^\[.*\]$|^\{.*\}$)").unwrap();
17}
18
19/// URL-decode a byte slice, replacing '+' with space and handling percent-encoding.
20///
21/// Optimized to avoid intermediate allocations by:
22/// - Processing bytes directly without intermediate String conversion
23/// - Using Cow to avoid allocation when no encoding is present
24/// - Replacing '+' during decoding rather than as a separate pass
25#[inline]
26fn url_decode_optimized(input: &[u8]) -> Cow<'_, str> {
27    let has_encoded = input.iter().any(|&b| b == b'+' || b == b'%');
28
29    if !has_encoded {
30        return match std::str::from_utf8(input) {
31            Ok(s) => Cow::Borrowed(s),
32            Err(_) => Cow::Owned(String::from_utf8_lossy(input).into_owned()),
33        };
34    }
35
36    let mut result = Vec::with_capacity(input.len());
37    let mut i = 0;
38
39    while i < input.len() {
40        match input[i] {
41            b'+' => {
42                result.push(b' ');
43                i += 1;
44            }
45            b'%' if i + 2 < input.len() => {
46                if let (Some(hi), Some(lo)) = (
47                    char::from(input[i + 1]).to_digit(16),
48                    char::from(input[i + 2]).to_digit(16),
49                ) {
50                    result.push((hi * 16 + lo) as u8);
51                    i += 3;
52                } else {
53                    result.push(input[i]);
54                    i += 1;
55                }
56            }
57            b => {
58                result.push(b);
59                i += 1;
60            }
61        }
62    }
63
64    Cow::Owned(String::from_utf8_lossy(&result).into_owned())
65}
66
67/// Parse a query string into a vector of (key, value) tuples.
68///
69/// Handles URL encoding and supports multiple values for the same key.
70///
71/// # Arguments
72/// * `qs` - The query string bytes
73/// * `separator` - The separator character (typically '&')
74///
75/// # Example
76/// ```ignore
77/// let result = parse_query_string(b"foo=1&foo=2&bar=test", '&');
78/// // vec![("foo", "1"), ("foo", "2"), ("bar", "test")]
79/// ```
80///
81/// # Performance
82/// Optimized to minimize allocations by:
83/// - Processing bytes directly without intermediate String allocation
84/// - Using custom URL decoder that handles '+' replacement in one pass
85/// - Pre-allocating result vector
86#[inline]
87pub fn parse_query_string(qs: &[u8], separator: char) -> Vec<(String, String)> {
88    if qs.is_empty() {
89        return Vec::new();
90    }
91
92    let separator_byte = separator as u8;
93    let mut result = Vec::with_capacity(8);
94
95    let mut start = 0;
96    let mut i = 0;
97
98    while i <= qs.len() {
99        if i == qs.len() || qs[i] == separator_byte {
100            if i > start {
101                let pair = &qs[start..i];
102
103                if let Some(eq_pos) = pair.iter().position(|&b| b == b'=') {
104                    let key = url_decode_optimized(&pair[..eq_pos]);
105                    let value = url_decode_optimized(&pair[eq_pos + 1..]);
106                    result.push((key.into_owned(), value.into_owned()));
107                } else {
108                    let key = url_decode_optimized(pair);
109                    result.push((key.into_owned(), String::new()));
110                }
111            }
112
113            start = i + 1;
114        }
115
116        i += 1;
117    }
118
119    result
120}
121
122/// Decode a string value into a JSON Value with type conversion.
123///
124/// Handles:
125/// - JSON objects and arrays (if wrapped in brackets)
126/// - Booleans (true/false/1/0, case-insensitive)
127/// - Null
128/// - Numbers (if parse_numbers is true)
129/// - Strings (fallback)
130#[inline]
131fn decode_value(json_str: String, parse_numbers: bool) -> Value {
132    if PARENTHESES_RE.is_match(json_str.as_str()) {
133        let result: Value = match from_str(json_str.as_str()) {
134            Ok(value) => value,
135            Err(_) => match from_str(json_str.replace('\'', "\"").as_str()) {
136                Ok(normalized) => normalized,
137                Err(_) => Value::Null,
138            },
139        };
140        return result;
141    }
142
143    let normalized = json_str.replace('"', "");
144
145    let json_boolean = parse_boolean(&normalized);
146    let json_null = Ok::<_, Infallible>(normalized == "null");
147
148    if parse_numbers {
149        let json_integer = normalized.parse::<i64>();
150        let json_float = normalized.parse::<f64>();
151        return match (json_integer, json_float, json_boolean, json_null) {
152            (Ok(json_integer), _, _, _) => Value::from(json_integer),
153            (_, Ok(json_float), _, _) => Value::from(json_float),
154            (_, _, Ok(json_boolean), _) => Value::from(json_boolean),
155            (_, _, _, Ok(true)) => Value::Null,
156            _ => Value::from(normalized),
157        };
158    }
159
160    match (json_boolean, json_null) {
161        (Ok(json_boolean), _) => Value::from(json_boolean),
162        (_, Ok(true)) => Value::Null,
163        _ => Value::from(normalized),
164    }
165}
166
167/// Parse a boolean value from a string.
168///
169/// Accepts:
170/// - "true" (case-insensitive) → true
171/// - "false" (case-insensitive) → false
172/// - "1" → true
173/// - "0" → false
174/// - "" (empty string) → Err (don't coerce, preserve as empty string)
175#[inline]
176fn parse_boolean(s: &str) -> Result<bool, ()> {
177    let lower = s.to_lowercase();
178    if lower == "true" || s == "1" {
179        Ok(true)
180    } else if lower == "false" || s == "0" {
181        Ok(false)
182    } else {
183        Err(())
184    }
185}
186
187/// Parse a query string into a JSON Value.
188///
189/// This function:
190/// - Handles multiple values for the same key (creates arrays)
191/// - Auto-converts types (numbers, booleans, null, objects, arrays)
192/// - Collapses single-item arrays into single values
193///
194/// # Arguments
195/// * `qs` - The query string bytes
196/// * `parse_numbers` - Whether to parse numeric strings into numbers
197///
198/// # Example
199/// ```ignore
200/// let result = parse_query_string_to_json(b"foo=1&foo=2&bar=test&active=true", true);
201/// // {"foo": [1, 2], "bar": "test", "active": true}
202/// ```
203#[inline]
204pub fn parse_query_string_to_json(qs: &[u8], parse_numbers: bool) -> Value {
205    let mut array_map: FxHashMap<String, Vec<Value>> = FxHashMap::default();
206
207    for (key, value) in parse_query_string(qs, '&') {
208        match array_map.get_mut(&key) {
209            Some(entry) => {
210                entry.push(decode_value(value, parse_numbers));
211            }
212            None => {
213                array_map.insert(key, vec![decode_value(value, parse_numbers)]);
214            }
215        }
216    }
217
218    array_map
219        .iter()
220        .map(|(key, value)| {
221            if value.len() == 1 {
222                (key, value[0].to_owned())
223            } else {
224                (key, Value::Array(value.to_owned()))
225            }
226        })
227        .collect::<Value>()
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233    use serde_json::{json, to_string};
234
235    fn eq_str(value: Value, string: &str) {
236        assert_eq!(&to_string(&value).unwrap_or_default(), string)
237    }
238
239    #[test]
240    fn test_ampersand_separator() {
241        assert_eq!(
242            parse_query_string(b"key=1&key=2&anotherKey=a&yetAnother=z", '&'),
243            vec![
244                (String::from("key"), String::from("1")),
245                (String::from("key"), String::from("2")),
246                (String::from("anotherKey"), String::from("a")),
247                (String::from("yetAnother"), String::from("z")),
248            ]
249        );
250    }
251
252    #[test]
253    fn test_handles_url_encoded_ampersand() {
254        assert_eq!(
255            parse_query_string(b"first=%26%40A.ac&second=aaa", '&'),
256            vec![
257                (String::from("first"), String::from("&@A.ac")),
258                (String::from("second"), String::from("aaa")),
259            ]
260        );
261    }
262
263    #[test]
264    fn parse_query_string_to_json_parses_simple_string() {
265        eq_str(parse_query_string_to_json(b"0=foo", true), r#"{"0":"foo"}"#);
266    }
267
268    #[test]
269    fn parse_query_string_to_json_parses_numbers() {
270        assert_eq!(parse_query_string_to_json(b"a=1", true), json!({"a": 1}));
271        assert_eq!(parse_query_string_to_json(b"a=1.1", true), json!({"a": 1.1}));
272    }
273
274    #[test]
275    fn parse_query_string_to_json_parses_booleans() {
276        assert_eq!(parse_query_string_to_json(b"a=true", false), json!({"a": true}));
277        assert_eq!(parse_query_string_to_json(b"a=false", false), json!({"a": false}));
278    }
279
280    #[test]
281    fn parse_query_string_to_json_parses_booleans_from_numbers() {
282        assert_eq!(parse_query_string_to_json(b"a=1", false), json!({"a": true}));
283        assert_eq!(parse_query_string_to_json(b"a=0", false), json!({"a": false}));
284    }
285
286    #[test]
287    fn parse_query_string_to_json_parses_case_insensitive_booleans() {
288        assert_eq!(parse_query_string_to_json(b"a=True", false), json!({"a": true}));
289        assert_eq!(parse_query_string_to_json(b"a=TRUE", false), json!({"a": true}));
290        assert_eq!(parse_query_string_to_json(b"a=False", false), json!({"a": false}));
291        assert_eq!(parse_query_string_to_json(b"a=FALSE", false), json!({"a": false}));
292    }
293
294    #[test]
295    fn parse_query_string_to_json_parses_multiple_values() {
296        assert_eq!(
297            parse_query_string_to_json(b"a=1&a=2&a=3", true),
298            json!({ "a": [1,2,3] })
299        );
300    }
301
302    #[test]
303    fn parse_query_string_to_json_parses_null() {
304        assert_eq!(parse_query_string_to_json(b"a=null", true), json!({ "a": null }));
305    }
306
307    #[test]
308    fn parse_query_string_to_json_parses_empty_string() {
309        assert_eq!(parse_query_string_to_json(b"a=", true), json!({ "a": "" }));
310    }
311
312    #[test]
313    fn parse_query_string_to_json_parses_empty_string_without_number_parsing() {
314        assert_eq!(parse_query_string_to_json(b"a=", false), json!({ "a": "" }));
315    }
316
317    #[test]
318    fn parse_query_string_to_json_parses_multiple_string_values() {
319        assert_eq!(
320            parse_query_string_to_json(b"q=foo&q=bar", true),
321            json!({ "q": ["foo", "bar"] })
322        );
323    }
324
325    #[test]
326    fn parse_query_string_to_json_parses_multiple_string_values_with_parse_numbers_false() {
327        assert_eq!(
328            parse_query_string_to_json(b"q=foo&q=bar", false),
329            json!({ "q": ["foo", "bar"] })
330        );
331    }
332
333    #[test]
334    fn parse_query_string_to_json_preserves_order_and_duplicates() {
335        assert_eq!(
336            parse_query_string_to_json(b"q=foo&q=bar&q=baz", true),
337            json!({ "q": ["foo", "bar", "baz"] })
338        );
339
340        assert_eq!(
341            parse_query_string_to_json(b"q=foo&q=foo&q=bar", true),
342            json!({ "q": ["foo", "foo", "bar"] })
343        );
344    }
345
346    #[test]
347    fn test_url_encoded_special_chars_in_values() {
348        let result = parse_query_string_to_json(b"email=x%40test.com&special=%26%40A.ac", false);
349        assert_eq!(
350            result,
351            json!({
352                "email": "x@test.com",
353                "special": "&@A.ac"
354            })
355        );
356    }
357
358    #[test]
359    fn test_url_encoded_space() {
360        let result = parse_query_string_to_json(b"name=hello%20world", false);
361        assert_eq!(result, json!({ "name": "hello world" }));
362    }
363
364    #[test]
365    fn test_url_encoded_complex_chars() {
366        let result = parse_query_string_to_json(b"name=test%26value%3D123", false);
367        assert_eq!(result, json!({ "name": "test&value=123" }));
368    }
369
370    #[test]
371    fn test_malformed_percent_single_char() {
372        let result = parse_query_string(b"key=%", '&');
373        assert_eq!(result, vec![(String::from("key"), String::from("%"))]);
374    }
375
376    #[test]
377    fn test_malformed_percent_single_hex_only() {
378        let result = parse_query_string(b"key=%2", '&');
379        assert_eq!(result, vec![(String::from("key"), String::from("%2"))]);
380    }
381
382    #[test]
383    fn test_malformed_percent_invalid_hex_chars() {
384        let result = parse_query_string(b"key=%GG&other=value", '&');
385        assert_eq!(
386            result,
387            vec![
388                (String::from("key"), String::from("%GG")),
389                (String::from("other"), String::from("value")),
390            ]
391        );
392    }
393
394    #[test]
395    fn test_malformed_percent_mixed_invalid_hex() {
396        let result = parse_query_string(b"key=%2G&other=value", '&');
397        assert_eq!(
398            result,
399            vec![
400                (String::from("key"), String::from("%2G")),
401                (String::from("other"), String::from("value")),
402            ]
403        );
404    }
405
406    #[test]
407    fn test_percent_encoding_lowercase_hex() {
408        let result = parse_query_string(b"key=%2f&other=test", '&');
409        assert_eq!(
410            result,
411            vec![
412                (String::from("key"), String::from("/")),
413                (String::from("other"), String::from("test")),
414            ]
415        );
416    }
417
418    #[test]
419    fn test_percent_encoding_uppercase_hex() {
420        let result = parse_query_string(b"key=%2F&other=test", '&');
421        assert_eq!(
422            result,
423            vec![
424                (String::from("key"), String::from("/")),
425                (String::from("other"), String::from("test")),
426            ]
427        );
428    }
429
430    #[test]
431    fn test_percent_encoding_mixed_case_hex() {
432        let result = parse_query_string(b"key=%2f%3D%4A", '&');
433        assert_eq!(result, vec![(String::from("key"), String::from("/=J"))]);
434    }
435
436    #[test]
437    fn test_plus_as_space_in_value() {
438        let result = parse_query_string(b"message=hello+world", '&');
439        assert_eq!(result, vec![(String::from("message"), String::from("hello world"))]);
440    }
441
442    #[test]
443    fn test_plus_as_space_multiple_plus() {
444        let result = parse_query_string(b"message=a+b+c+d", '&');
445        assert_eq!(result, vec![(String::from("message"), String::from("a b c d"))]);
446    }
447
448    #[test]
449    fn test_percent_encoded_space_vs_plus() {
450        let result = parse_query_string(b"a=%20space&b=+plus", '&');
451        assert_eq!(
452            result,
453            vec![
454                (String::from("a"), String::from(" space")),
455                (String::from("b"), String::from(" plus")),
456            ]
457        );
458    }
459
460    #[test]
461    fn test_mixed_plus_and_percent_encoded_space() {
462        let result = parse_query_string(b"text=hello+%20world", '&');
463        assert_eq!(result, vec![(String::from("text"), String::from("hello  world"))]);
464    }
465
466    #[test]
467    fn test_ampersand_in_value_encoded() {
468        let result = parse_query_string(b"text=foo%26bar", '&');
469        assert_eq!(result, vec![(String::from("text"), String::from("foo&bar"))]);
470    }
471
472    #[test]
473    fn test_equals_in_value_encoded() {
474        let result = parse_query_string(b"text=a%3Db", '&');
475        assert_eq!(result, vec![(String::from("text"), String::from("a=b"))]);
476    }
477
478    #[test]
479    fn test_question_mark_in_value_encoded() {
480        let result = parse_query_string(b"text=what%3F", '&');
481        assert_eq!(result, vec![(String::from("text"), String::from("what?"))]);
482    }
483
484    #[test]
485    fn test_hash_in_value_encoded() {
486        let result = parse_query_string(b"text=anchor%23top", '&');
487        assert_eq!(result, vec![(String::from("text"), String::from("anchor#top"))]);
488    }
489
490    #[test]
491    fn test_multiple_encoded_special_chars() {
492        let result = parse_query_string(b"text=%26%3D%3F%23", '&');
493        assert_eq!(result, vec![(String::from("text"), String::from("&=?#"))]);
494    }
495
496    #[test]
497    fn test_empty_query_string() {
498        let result = parse_query_string(b"", '&');
499        assert_eq!(result, vec![]);
500    }
501
502    #[test]
503    fn test_multiple_consecutive_separators() {
504        let result = parse_query_string(b"a=1&&&b=2", '&');
505        assert_eq!(
506            result,
507            vec![
508                (String::from("a"), String::from("1")),
509                (String::from("b"), String::from("2")),
510            ]
511        );
512    }
513
514    #[test]
515    fn test_key_without_value() {
516        let result = parse_query_string(b"key=", '&');
517        assert_eq!(result, vec![(String::from("key"), String::from(""))]);
518    }
519
520    #[test]
521    fn test_key_without_equals() {
522        let result = parse_query_string(b"key", '&');
523        assert_eq!(result, vec![(String::from("key"), String::from(""))]);
524    }
525
526    #[test]
527    fn test_value_without_key() {
528        let result = parse_query_string(b"=value", '&');
529        assert_eq!(result, vec![(String::from(""), String::from("value"))]);
530    }
531
532    #[test]
533    fn test_multiple_equals_in_pair() {
534        let result = parse_query_string(b"key=val=more", '&');
535        assert_eq!(result, vec![(String::from("key"), String::from("val=more"))]);
536    }
537
538    #[test]
539    fn test_separator_at_start() {
540        let result = parse_query_string(b"&key=value", '&');
541        assert_eq!(result, vec![(String::from("key"), String::from("value"))]);
542    }
543
544    #[test]
545    fn test_separator_at_end() {
546        let result = parse_query_string(b"key=value&", '&');
547        assert_eq!(result, vec![(String::from("key"), String::from("value"))]);
548    }
549
550    #[test]
551    fn test_separator_at_both_ends() {
552        let result = parse_query_string(b"&key=value&", '&');
553        assert_eq!(result, vec![(String::from("key"), String::from("value"))]);
554    }
555
556    #[test]
557    fn test_multiple_values_same_key() {
558        let result = parse_query_string(b"tag=foo&tag=bar&tag=baz", '&');
559        assert_eq!(
560            result,
561            vec![
562                (String::from("tag"), String::from("foo")),
563                (String::from("tag"), String::from("bar")),
564                (String::from("tag"), String::from("baz")),
565            ]
566        );
567    }
568
569    #[test]
570    fn test_multiple_values_mixed_keys() {
571        let result = parse_query_string(b"tag=foo&id=1&tag=bar&id=2", '&');
572        assert_eq!(
573            result,
574            vec![
575                (String::from("tag"), String::from("foo")),
576                (String::from("id"), String::from("1")),
577                (String::from("tag"), String::from("bar")),
578                (String::from("id"), String::from("2")),
579            ]
580        );
581    }
582
583    #[test]
584    fn test_json_conversion_empty_key() {
585        let result = parse_query_string_to_json(b"=value", false);
586        assert_eq!(result, json!({ "": "value" }));
587    }
588
589    #[test]
590    fn test_json_conversion_all_empty_values() {
591        let result = parse_query_string_to_json(b"a=&b=&c=", false);
592        assert_eq!(result, json!({ "a": "", "b": "", "c": "" }));
593    }
594
595    #[test]
596    fn test_json_conversion_malformed_json_object() {
597        let result = parse_query_string_to_json(b"data={invalid", false);
598        assert_eq!(result, json!({ "data": "{invalid" }));
599    }
600
601    #[test]
602    fn test_json_conversion_malformed_json_array() {
603        let result = parse_query_string_to_json(b"items=[1,2,", false);
604        assert_eq!(result, json!({ "items": "[1,2," }));
605    }
606
607    #[test]
608    fn test_json_conversion_with_quotes_in_value() {
609        let result = parse_query_string_to_json(b"text=\"hello\"", false);
610        assert_eq!(result, json!({ "text": "hello" }));
611    }
612
613    #[test]
614    fn test_json_conversion_single_quotes_in_object() {
615        let result = parse_query_string_to_json(b"obj={'key':'value'}", false);
616        let value = result.get("obj");
617        assert!(value.is_some());
618    }
619
620    #[test]
621    fn test_boolean_case_insensitive_variations() {
622        assert_eq!(parse_query_string_to_json(b"a=tRuE", false), json!({"a": true}));
623        assert_eq!(parse_query_string_to_json(b"a=FaLsE", false), json!({"a": false}));
624        assert_eq!(
625            parse_query_string_to_json(b"a=tRuE&b=FaLsE", false),
626            json!({"a": true, "b": false})
627        );
628    }
629
630    #[test]
631    fn test_boolean_with_numbers_no_parse() {
632        assert_eq!(parse_query_string_to_json(b"a=1", false), json!({"a": true}));
633        assert_eq!(parse_query_string_to_json(b"a=0", false), json!({"a": false}));
634    }
635
636    #[test]
637    fn test_number_parsing_negative() {
638        assert_eq!(parse_query_string_to_json(b"a=-123", true), json!({"a": -123}));
639        assert_eq!(parse_query_string_to_json(b"a=-1.5", true), json!({"a": -1.5}));
640    }
641
642    #[test]
643    fn test_number_parsing_zero() {
644        assert_eq!(parse_query_string_to_json(b"a=0", true), json!({"a": 0}));
645        assert_eq!(parse_query_string_to_json(b"a=0.0", true), json!({"a": 0.0}));
646    }
647
648    #[test]
649    fn test_number_parsing_scientific_notation() {
650        assert_eq!(parse_query_string_to_json(b"a=1e10", true), json!({"a": 1e10}));
651        assert_eq!(parse_query_string_to_json(b"a=1.5e-3", true), json!({"a": 1.5e-3}));
652    }
653
654    #[test]
655    fn test_array_mixed_types_with_number_parsing() {
656        assert_eq!(
657            parse_query_string_to_json(b"vals=1&vals=2.5&vals=true&vals=test", true),
658            json!({"vals": [1, 2.5, true, "test"]})
659        );
660    }
661
662    #[test]
663    fn test_array_mixed_types_without_number_parsing() {
664        assert_eq!(
665            parse_query_string_to_json(b"vals=1&vals=2.5&vals=true&vals=test", false),
666            json!({"vals": [true, "2.5", true, "test"]})
667        );
668    }
669
670    #[test]
671    fn test_utf8_chinese_characters() {
672        let result = parse_query_string("name=中文".as_bytes(), '&');
673        assert_eq!(result, vec![(String::from("name"), String::from("中文"))]);
674    }
675
676    #[test]
677    fn test_utf8_emoji() {
678        let result = parse_query_string("emoji=🚀".as_bytes(), '&');
679        assert_eq!(result, vec![(String::from("emoji"), String::from("🚀"))]);
680    }
681
682    #[test]
683    fn test_utf8_mixed_with_encoding() {
684        let result = parse_query_string("text=hello%20中文".as_bytes(), '&');
685        assert_eq!(result, vec![(String::from("text"), String::from("hello 中文"))]);
686    }
687
688    #[test]
689    fn test_custom_separator_semicolon() {
690        let result = parse_query_string(b"a=1;b=2;c=3", ';');
691        assert_eq!(
692            result,
693            vec![
694                (String::from("a"), String::from("1")),
695                (String::from("b"), String::from("2")),
696                (String::from("c"), String::from("3")),
697            ]
698        );
699    }
700
701    #[test]
702    fn test_custom_separator_comma() {
703        let result = parse_query_string(b"a=1,b=2,c=3", ',');
704        assert_eq!(
705            result,
706            vec![
707                (String::from("a"), String::from("1")),
708                (String::from("b"), String::from("2")),
709                (String::from("c"), String::from("3")),
710            ]
711        );
712    }
713
714    #[test]
715    fn test_percent_encoding_all_byte_values() {
716        let result = parse_query_string(b"space=%20&at=%40&hash=%23&dollar=%24", '&');
717        assert_eq!(
718            result,
719            vec![
720                (String::from("space"), String::from(" ")),
721                (String::from("at"), String::from("@")),
722                (String::from("hash"), String::from("#")),
723                (String::from("dollar"), String::from("$")),
724            ]
725        );
726    }
727
728    #[test]
729    fn test_high_byte_values_in_percent_encoding() {
730        let result = parse_query_string(b"high=%ff%fe%fd", '&');
731        assert_eq!(result.len(), 1);
732        assert_eq!(result[0].0, "high");
733    }
734
735    #[test]
736    fn test_very_long_query_string() {
737        let mut long_query = String::from("key=");
738        long_query.push_str(&"a".repeat(10000));
739        let result = parse_query_string(long_query.as_bytes(), '&');
740        assert_eq!(result.len(), 1);
741        assert_eq!(result[0].0, "key");
742        assert_eq!(result[0].1.len(), 10000);
743    }
744
745    #[test]
746    fn test_very_large_number_of_parameters() {
747        let mut query = String::new();
748        for i in 0..100 {
749            if i > 0 {
750                query.push('&');
751            }
752            query.push_str(&format!("param{}=value{}", i, i));
753        }
754        let result = parse_query_string(query.as_bytes(), '&');
755        assert_eq!(result.len(), 100);
756        assert_eq!(result[0].0, "param0");
757        assert_eq!(result[99].0, "param99");
758    }
759
760    #[test]
761    fn test_literal_space_in_value() {
762        let result = parse_query_string(b"name=hello world", '&');
763        assert_eq!(result, vec![(String::from("name"), String::from("hello world"))]);
764    }
765
766    #[test]
767    fn test_tab_in_value() {
768        let result = parse_query_string(b"name=hello\tworld", '&');
769        assert_eq!(result, vec![(String::from("name"), String::from("hello\tworld"))]);
770    }
771
772    #[test]
773    fn test_newline_in_value() {
774        let result = parse_query_string(b"name=hello\nworld", '&');
775        assert_eq!(result, vec![(String::from("name"), String::from("hello\nworld"))]);
776    }
777}