Skip to main content

rupg_types/
multirange.rs

1//! The text and binary forms of the multirange types, from `multirange_in`, `multirange_out`, `multirange_recv` and `multirange_send` of `multirangetypes.c`. A multirange keeps its ranges in order, and these functions give and take the text or the bytes of each range. The caller reads and writes the ranges with the functions of the range type.
2
3use rupg_common::SqlState;
4
5use crate::binary::Recv;
6use crate::error::TypeError;
7
8/// The text of an empty range.
9const EMPTY_LITERAL: &[u8] = b"empty";
10
11/// `isspace` of C in the C locale.
12fn is_space(b: u8) -> bool {
13    matches!(b, b' ' | b'\t' | b'\n' | b'\r' | 0x0B | 0x0C)
14}
15
16/// The error of a multirange literal that does not parse.
17fn 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/// The states of the parse of `multirange_in`.
27#[derive(Clone, Copy, PartialEq, Eq)]
28enum State {
29    BeforeRange,
30    InRange,
31    InRangeEscaped,
32    InRangeQuoted,
33    InRangeQuotedEscaped,
34    AfterRange,
35    Finished,
36}
37
38/// `multirange_in`: a left brace, zero or more ranges with commas between them, and a right brace. White space can be before and after each part. The parse gives the text of each range that is not the key word `empty` to `range` when it finds the end of that range, so the error of a range comes before the errors of the text after it.
39///
40/// # Errors
41///
42/// `22P02` "malformed multirange literal" with the detail of PostgreSQL, and the errors of `range`.
43pub 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
130/// `multirange_out`: the ranges in braces with a comma between them.
131pub 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
142/// `multirange_recv`: the number of ranges, then the length and the bytes of each range. The parse gives the bytes of each range to `range` in order.
143///
144/// # Errors
145///
146/// `08P01` for a value that is too short or too long, `XX000` for a number of ranges that `palloc` refuses, and the errors of `range`.
147pub 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
174/// `multirange_send`: the number of ranges, then the length and the bytes of each range.
175pub 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}