1use rupg_common::SqlState;
4
5use crate::binary::Recv;
6use crate::error::TypeError;
7
8const EMPTY_LITERAL: &[u8] = b"empty";
10
11fn is_space(b: u8) -> bool {
13 matches!(b, b' ' | b'\t' | b'\n' | b'\r' | 0x0B | 0x0C)
14}
15
16fn malformed(text: &str, detail: &str) -> TypeError {
18 let mut error = TypeError::new(
19 SqlState::INVALID_TEXT_REPRESENTATION,
20 format!("malformed multirange literal: \"{text}\""),
21 );
22 error.detail = Some(detail.to_string());
23 error
24}
25
26#[derive(Clone, Copy, PartialEq, Eq)]
28enum State {
29 BeforeRange,
30 InRange,
31 InRangeEscaped,
32 InRangeQuoted,
33 InRangeQuotedEscaped,
34 AfterRange,
35 Finished,
36}
37
38pub fn multirange_parse<E: From<TypeError>>(
44 text: &str,
45 mut range: impl FnMut(&str) -> Result<(), E>,
46) -> Result<(), E> {
47 let s = text.as_bytes();
48 let mut at = 0;
49 while at < s.len() && is_space(s[at]) {
50 at += 1;
51 }
52 if s.get(at) != Some(&b'{') {
53 return Err(malformed(text, "Missing left brace.").into());
54 }
55 at += 1;
56 let mut state = State::BeforeRange;
57 let mut seen = 0;
58 let mut start = 0;
59 while state != State::Finished {
60 let Some(&ch) = s.get(at) else {
61 return Err(malformed(text, "Unexpected end of input.").into());
62 };
63 if is_space(ch) {
64 at += 1;
65 continue;
66 }
67 match state {
68 State::BeforeRange => {
69 if ch == b'[' || ch == b'(' {
70 start = at;
71 state = State::InRange;
72 } else if ch == b'}' && seen == 0 {
73 state = State::Finished;
74 } else if s.len() - at >= EMPTY_LITERAL.len()
75 && s[at..at + EMPTY_LITERAL.len()].eq_ignore_ascii_case(EMPTY_LITERAL)
76 {
77 seen += 1;
78 at += EMPTY_LITERAL.len() - 1;
79 state = State::AfterRange;
80 } else {
81 return Err(malformed(text, "Expected range start.").into());
82 }
83 }
84 State::InRange => {
85 if ch == b']' || ch == b')' {
86 seen += 1;
87 range(&text[start..=at])?;
88 state = State::AfterRange;
89 } else if ch == b'"' {
90 state = State::InRangeQuoted;
91 } else if ch == b'\\' {
92 state = State::InRangeEscaped;
93 }
94 }
95 State::InRangeEscaped => state = State::InRange,
96 State::InRangeQuoted => {
97 if ch == b'"' {
98 if s.get(at + 1) == Some(&b'"') {
99 at += 1;
100 } else {
101 state = State::InRange;
102 }
103 } else if ch == b'\\' {
104 state = State::InRangeQuotedEscaped;
105 }
106 }
107 State::AfterRange => {
108 if ch == b',' {
109 state = State::BeforeRange;
110 } else if ch == b'}' {
111 state = State::Finished;
112 } else {
113 return Err(malformed(text, "Expected comma or end of multirange.").into());
114 }
115 }
116 State::InRangeQuotedEscaped => state = State::InRangeQuoted,
117 State::Finished => {}
118 }
119 at += 1;
120 }
121 while at < s.len() && is_space(s[at]) {
122 at += 1;
123 }
124 if at < s.len() {
125 return Err(malformed(text, "Junk after closing right brace.").into());
126 }
127 Ok(())
128}
129
130pub fn multirange_deparse<'a>(ranges: impl IntoIterator<Item = &'a [u8]>, out: &mut Vec<u8>) {
132 out.push(b'{');
133 for (i, range) in ranges.into_iter().enumerate() {
134 if i > 0 {
135 out.push(b',');
136 }
137 out.extend_from_slice(range);
138 }
139 out.push(b'}');
140}
141
142pub fn multirange_recv<'a, E: From<TypeError>>(
148 recv: &mut Recv<'a>,
149 mut range: impl FnMut(&'a [u8]) -> Result<(), E>,
150) -> Result<(), E> {
151 let count = recv.i32()?;
152 let size = i64::from(count).cast_unsigned().wrapping_mul(8);
153 if size > 0x3fff_ffff {
154 return Err(TypeError::new(
155 SqlState::INTERNAL_ERROR,
156 format!("invalid memory alloc request size {size}"),
157 )
158 .into());
159 }
160 for _ in 0..count {
161 let len = recv.u32()?;
162 range(recv.bytes(usize::try_from(len).unwrap_or(usize::MAX))?)?;
163 }
164 if recv.remaining() != 0 {
165 return Err(TypeError::new(
166 SqlState::PROTOCOL_VIOLATION,
167 "invalid message format".to_string(),
168 )
169 .into());
170 }
171 Ok(())
172}
173
174pub fn multirange_send(ranges: &[&[u8]], out: &mut Vec<u8>) {
176 let count = u32::try_from(ranges.len()).unwrap_or(u32::MAX);
177 out.extend_from_slice(&count.to_be_bytes());
178 for range in ranges {
179 let len = u32::try_from(range.len()).unwrap_or(u32::MAX);
180 out.extend_from_slice(&len.to_be_bytes());
181 out.extend_from_slice(range);
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 fn parse(text: &str) -> Result<Vec<String>, TypeError> {
190 let mut out = Vec::new();
191 multirange_parse(text, |r: &str| -> Result<(), TypeError> {
192 out.push(r.to_string());
193 Ok(())
194 })?;
195 Ok(out)
196 }
197
198 fn detail(text: &str) -> String {
199 parse(text).unwrap_err().detail.unwrap()
200 }
201
202 #[test]
203 fn text_forms() {
204 assert_eq!(parse(" { } ").unwrap(), Vec::<String>::new());
205 assert_eq!(parse("{ [1,5) , (8, 10] }").unwrap(), ["[1,5)", "(8, 10]"]);
206 assert_eq!(parse("{EMPTY,[1,2)}").unwrap(), ["[1,2)"]);
207 assert_eq!(parse(r#"{["a)",")\""]}"#).unwrap(), [r#"["a)",")\""]"#]);
208 assert_eq!(parse(r"{[a\),b)}").unwrap(), [r"[a\),b)"]);
209 assert_eq!(detail("[1,5)"), "Missing left brace.");
210 assert_eq!(detail("{[1,5)"), "Unexpected end of input.");
211 assert_eq!(detail("{,}"), "Expected range start.");
212 assert_eq!(detail("{[1,5),}"), "Expected range start.");
213 assert_eq!(detail("{[1,5) [6,7)}"), "Expected comma or end of multirange.");
214 assert_eq!(detail("{[1,5)}x"), "Junk after closing right brace.");
215 let mut out = Vec::new();
216 multirange_deparse([&b"[1,5)"[..], b"[8,10)"], &mut out);
217 assert_eq!(out, b"{[1,5),[8,10)}");
218 }
219
220 #[test]
221 fn binary_forms() {
222 let mut out = Vec::new();
223 multirange_send(&[&[1], &[2, 3]], &mut out);
224 assert_eq!(out, [0, 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 2, 2, 3]);
225 let mut ranges = Vec::new();
226 multirange_recv(&mut Recv::new(&out), |r| -> Result<(), TypeError> {
227 ranges.push(r);
228 Ok(())
229 })
230 .unwrap();
231 assert_eq!(ranges, [&[1][..], &[2, 3][..]]);
232 let error = multirange_recv(&mut Recv::new(&[0, 0, 0, 0, 9]), |_| Ok::<(), TypeError>(()));
233 assert_eq!(error.unwrap_err().message, "invalid message format");
234 let error =
235 multirange_recv(&mut Recv::new(&[255, 255, 255, 255]), |_| Ok::<(), TypeError>(()));
236 assert_eq!(
237 error.unwrap_err().message,
238 "invalid memory alloc request size 18446744073709551608"
239 );
240 }
241}