servo_url/
encoding.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::Cow;
6
7use encoding_rs::{EncoderResult, Encoding, UTF_8};
8
9/// This is equivalent to [Encoding::encode], except nonmappable code points are handled
10/// according to the url specification, which expects nonmappable code points to be wrapped in `%26%23` and
11/// `%3B` (see [percent encode after encoding](https://url.spec.whatwg.org/#string-percent-encode-after-encoding)).
12pub fn encode_as_url_query_string<'a>(
13    mut string: &'a str,
14    encoding: &'static Encoding,
15) -> Cow<'a, [u8]> {
16    let output_encoding = encoding.output_encoding();
17    if output_encoding == UTF_8 {
18        return Cow::Borrowed(string.as_bytes());
19    }
20
21    let bytes = string.as_bytes();
22    let valid_up_to = if output_encoding == encoding_rs::ISO_2022_JP {
23        Encoding::iso_2022_jp_ascii_valid_up_to(bytes)
24    } else {
25        Encoding::ascii_valid_up_to(bytes)
26    };
27
28    if valid_up_to == bytes.len() {
29        // All the bytes are already correctly encoded - we don't need to do anything!
30        return Cow::Borrowed(bytes);
31    }
32
33    let mut encoder = encoding.new_encoder();
34    let mut output = Vec::with_capacity(
35        encoder
36            .max_buffer_length_from_utf8_if_no_unmappables(string.len())
37            .expect("string size would overflow `usize`"),
38    );
39    loop {
40        match encoder.encode_from_utf8_to_vec_without_replacement(string, &mut output, true) {
41            (EncoderResult::InputEmpty, _) => break,
42            (EncoderResult::OutputFull, consumed) => {
43                output.reserve(
44                    encoder
45                        .max_buffer_length_from_utf8_if_no_unmappables(string.len())
46                        .expect("string size would overflow `usize`"),
47                );
48                string = &string[consumed..];
49            },
50            (EncoderResult::Unmappable(character), consumed) => {
51                use std::io::Write;
52                write!(&mut output, "%26%23{}%3B", character as u32).unwrap();
53                string = &string[consumed..];
54            },
55        };
56    }
57
58    Cow::Owned(output)
59}