1 | use core::str::from_utf8_unchecked; |
2 | |
3 | use alloc::borrow::Cow; |
4 | use alloc::string::String; |
5 | use alloc::vec::Vec; |
6 | |
7 | #[cfg (feature = "std" )] |
8 | use std::io::{self, Write}; |
9 | |
10 | use 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 | /// * `<` => `<` |
18 | /// * `>` => `>` |
19 | /// * `"` => `"` |
20 | /// |
21 | /// Other non-alphanumeric characters are escaped to `&#xHH;`. |
22 | pub 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 | /// * `&` => `&` |
64 | /// * `<` => `<` |
65 | /// * `>` => `>` |
66 | /// * `"` => `"` |
67 | /// |
68 | /// Other non-alphanumeric characters are escaped to `&#xHH;`. |
69 | #[inline ] |
70 | pub 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 | /// * `&` => `&` |
79 | /// * `<` => `<` |
80 | /// * `>` => `>` |
81 | /// * `"` => `"` |
82 | /// |
83 | /// Other non-alphanumeric characters are escaped to `&#xHH;`. |
84 | pub 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 | /// * `&` => `&` |
121 | /// * `<` => `<` |
122 | /// * `>` => `>` |
123 | /// * `"` => `"` |
124 | /// |
125 | /// Other non-alphanumeric characters are escaped to `&#xHH;`. |
126 | pub 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 | |