1use core::str::from_utf8_unchecked;
2
3use alloc::borrow::Cow;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7#[cfg(feature = "std")]
8use std::io::{self, Write};
9
10use crate::functions::*;
11
12/// Encode text used in an unquoted attribute. Except for alphanumeric characters, escape all characters which are less than 128.
13///
14/// The following characters are escaped to named entities:
15///
16/// * `&` => `&`
17/// * `<` => `&lt;`
18/// * `>` => `&gt;`
19/// * `"` => `&quot;`
20///
21/// Other non-alphanumeric characters are escaped to `&#xHH;`.
22pub fn encode_unquoted_attribute<S: ?Sized + AsRef<str>>(text: &S) -> Cow<str> {
23 let text = text.as_ref();
24 let text_bytes = text.as_bytes();
25
26 let text_length = text_bytes.len();
27
28 let mut p = 0;
29 let mut e;
30
31 loop {
32 if p == text_length {
33 return Cow::from(text);
34 }
35
36 e = text_bytes[p];
37
38 if utf8_width::is_width_1(e) && !e.is_ascii_alphanumeric() {
39 break;
40 }
41
42 p += 1;
43 }
44
45 let mut v = Vec::with_capacity(text_length);
46
47 v.extend_from_slice(&text_bytes[..p]);
48
49 write_html_entity_to_vec(e, &mut v);
50
51 encode_unquoted_attribute_to_vec(
52 unsafe { from_utf8_unchecked(&text_bytes[(p + 1)..]) },
53 &mut v,
54 );
55
56 Cow::from(unsafe { String::from_utf8_unchecked(v) })
57}
58
59/// Write text used in an unquoted attribute to a mutable `String` reference and return the encoded string slice. Except for alphanumeric characters, escape all characters which are less than 128.
60///
61/// The following characters are escaped to named entities:
62///
63/// * `&` => `&amp;`
64/// * `<` => `&lt;`
65/// * `>` => `&gt;`
66/// * `"` => `&quot;`
67///
68/// Other non-alphanumeric characters are escaped to `&#xHH;`.
69#[inline]
70pub fn encode_unquoted_attribute_to_string<S: AsRef<str>>(text: S, output: &mut String) -> &str {
71 unsafe { from_utf8_unchecked(encode_unquoted_attribute_to_vec(text, output.as_mut_vec())) }
72}
73
74/// Write text used in an unquoted attribute to a mutable `Vec<u8>` reference and return the encoded data slice. Except for alphanumeric characters, escape all characters which are less than 128.
75///
76/// The following characters are escaped to named entities:
77///
78/// * `&` => `&amp;`
79/// * `<` => `&lt;`
80/// * `>` => `&gt;`
81/// * `"` => `&quot;`
82///
83/// Other non-alphanumeric characters are escaped to `&#xHH;`.
84pub fn encode_unquoted_attribute_to_vec<S: AsRef<str>>(text: S, output: &mut Vec<u8>) -> &[u8] {
85 let text = text.as_ref();
86 let text_bytes = text.as_bytes();
87 let text_length = text_bytes.len();
88
89 output.reserve(text_length);
90
91 let current_length = output.len();
92
93 let mut p = 0;
94 let mut e;
95
96 let mut start = 0;
97
98 while p < text_length {
99 e = text_bytes[p];
100
101 if utf8_width::is_width_1(e) && !e.is_ascii_alphanumeric() {
102 output.extend_from_slice(&text_bytes[start..p]);
103 start = p + 1;
104 write_html_entity_to_vec(e, output);
105 }
106
107 p += 1;
108 }
109
110 output.extend_from_slice(&text_bytes[start..p]);
111
112 &output[current_length..]
113}
114
115#[cfg(feature = "std")]
116/// Write text used in an unquoted attribute to a writer. Except for alphanumeric characters, escape all characters which are less than 128.
117///
118/// The following characters are escaped to named entities:
119///
120/// * `&` => `&amp;`
121/// * `<` => `&lt;`
122/// * `>` => `&gt;`
123/// * `"` => `&quot;`
124///
125/// Other non-alphanumeric characters are escaped to `&#xHH;`.
126pub fn encode_unquoted_attribute_to_writer<S: AsRef<str>, W: Write>(
127 text: S,
128 output: &mut W,
129) -> Result<(), io::Error> {
130 let text: &str = text.as_ref();
131 let text_bytes: &[u8] = text.as_bytes();
132 let text_length: usize = text_bytes.len();
133
134 let mut p: usize = 0;
135 let mut e: u8;
136
137 let mut start: usize = 0;
138
139 while p < text_length {
140 e = text_bytes[p];
141
142 if utf8_width::is_width_1(byte:e) && !e.is_ascii_alphanumeric() {
143 output.write_all(&text_bytes[start..p])?;
144 start = p + 1;
145 write_html_entity_to_writer(e, output)?;
146 }
147
148 p += 1;
149 }
150
151 output.write_all(&text_bytes[start..p])
152}
153