Skip to main content

rupg_types/
bit.rs

1//! `bit` and `bit varying`, in the text and binary formats of `src/backend/utils/adt/varbit.c`. A value is a string of `0` and `1`, one character for each bit, so that the text form is the value and the order of the strings is the order of `bitcmp`.
2
3use rupg_common::SqlState;
4
5use crate::binary::Recv;
6use crate::error::TypeError;
7
8/// `VARBITMAXLEN`: the most bits in a value.
9pub const BIT_MAX_LEN: usize = 2_147_483_640;
10
11/// The type name in the errors of a length.
12fn bit_type(varying: bool, typmod: i32) -> String {
13    if varying { format!("bit varying({typmod})") } else { format!("bit({typmod})") }
14}
15
16/// `22026` for `bit` or `22001` for `bit varying`: the length does not agree with the typmod.
17fn length_error(len: usize, typmod: i32, varying: bool) -> TypeError {
18    if varying {
19        TypeError::new(
20            SqlState::STRING_DATA_RIGHT_TRUNCATION,
21            format!("bit string too long for type {}", bit_type(true, typmod)),
22        )
23    } else {
24        TypeError::new(
25            SqlState::STRING_DATA_LENGTH_MISMATCH,
26            format!("bit string length {len} does not match type {}", bit_type(false, typmod)),
27        )
28    }
29}
30
31/// The check of a length against a typmod: `bit(n)` takes exactly `n` bits and `bit varying(n)` takes no more than `n` bits. A typmod of 0 or less has no limit.
32fn check_length(len: usize, typmod: i32, varying: bool) -> Result<(), TypeError> {
33    let Ok(limit) = usize::try_from(typmod) else { return Ok(()) };
34    if limit == 0 || len == limit || varying && len < limit {
35        return Ok(());
36    }
37    Err(length_error(len, typmod, varying))
38}
39
40/// The character at byte `at` of `s`, for an error.
41fn char_at(s: &str, at: usize) -> &str {
42    let len = s[at..].chars().next().map_or(0, char::len_utf8);
43    &s[at..at + len]
44}
45
46/// `bit_in` and `varbit_in`: the bits after a `B`, the hex digits after an `X`, or the bits with no prefix. The length check comes before the check of the digits, and the length of a value with no prefix is its length in bytes.
47///
48/// # Errors
49///
50/// `22P02` for a digit that is not valid, `22026` for a `bit(n)` value that does not have `n` bits, `22001` for a `bit varying(n)` value with more than `n` bits, and `54000` for a hex value that is too long.
51pub fn bit_in(s: &str, typmod: i32, varying: bool) -> Result<String, TypeError> {
52    let (hex, digits) = match s.as_bytes().first() {
53        Some(b'b' | b'B') => (false, &s[1..]),
54        Some(b'x' | b'X') => (true, &s[1..]),
55        _ => (false, s),
56    };
57    let start = s.len() - digits.len();
58    let len = if hex {
59        if digits.len() > BIT_MAX_LEN / 4 {
60            return Err(TypeError::new(
61                SqlState::PROGRAM_LIMIT_EXCEEDED,
62                format!("bit string length exceeds the maximum allowed ({BIT_MAX_LEN})"),
63            ));
64        }
65        digits.len() * 4
66    } else {
67        digits.len()
68    };
69    check_length(len, typmod, varying)?;
70    let mut out = String::with_capacity(len);
71    for (i, c) in digits.bytes().enumerate() {
72        if hex {
73            let Some(v) = char::from(c).to_digit(16).filter(|_| c.is_ascii()) else {
74                return Err(TypeError::new(
75                    SqlState::INVALID_TEXT_REPRESENTATION,
76                    format!("\"{}\" is not a valid hexadecimal digit", char_at(s, start + i)),
77                ));
78            };
79            out.push_str(&format!("{v:04b}"));
80        } else if c == b'0' || c == b'1' {
81            out.push(char::from(c));
82        } else {
83            return Err(TypeError::new(
84                SqlState::INVALID_TEXT_REPRESENTATION,
85                format!("\"{}\" is not a valid binary digit", char_at(s, start + i)),
86            ));
87        }
88    }
89    Ok(out)
90}
91
92/// `bit` and `varbit`, the length casts: an explicit cast pads `bit(n)` with zeros or cuts it to `n` bits and cuts `bit varying(n)` to `n` bits. Other casts give the error of the input.
93///
94/// # Errors
95///
96/// `22026` or `22001` for a value that does not fit in a cast that is not explicit.
97pub fn bit_coerce(
98    bits: &str,
99    typmod: i32,
100    varying: bool,
101    explicit: bool,
102) -> Result<String, TypeError> {
103    if check_length(bits.len(), typmod, varying).is_ok() {
104        return Ok(bits.to_string());
105    }
106    if !explicit {
107        return Err(length_error(bits.len(), typmod, varying));
108    }
109    let len = usize::try_from(typmod).unwrap_or(0);
110    let mut out: String = bits.chars().take(len).collect();
111    while out.len() < len {
112        out.push('0');
113    }
114    Ok(out)
115}
116
117/// `bit_recv` and `varbit_recv`: the number of bits as an `int4`, then the bits in bytes, with the last byte padded with zero bits.
118///
119/// # Errors
120///
121/// `22P03` for a length that is not valid, the errors of [`bit_in`] for a length that does not agree with the typmod, and `08P01` for a value with too few bytes.
122pub fn bit_recv(recv: &mut Recv<'_>, typmod: i32, varying: bool) -> Result<String, TypeError> {
123    let len = recv.i32()?;
124    let Some(len) = usize::try_from(len).ok().filter(|&len| len <= BIT_MAX_LEN) else {
125        return Err(TypeError::new(
126            SqlState::INVALID_BINARY_REPRESENTATION,
127            "invalid length in external bit string".to_string(),
128        ));
129    };
130    check_length(len, typmod, varying)?;
131    let bytes = recv.bytes(len.div_ceil(8))?;
132    Ok((0..len).map(|i| if (bytes[i / 8] >> (7 - i % 8)) & 1 == 1 { '1' } else { '0' }).collect())
133}
134
135/// The bits in bytes, from the high bit of the first byte, with the last byte padded with zero bits.
136#[must_use]
137pub fn bit_bytes(bits: &str) -> Vec<u8> {
138    let mut out = vec![0u8; bits.len().div_ceil(8)];
139    for (i, c) in bits.bytes().enumerate() {
140        if c == b'1' {
141            out[i / 8] |= 0x80 >> (i % 8);
142        }
143    }
144    out
145}
146
147/// `bit_send` and `varbit_send`.
148pub fn bit_send(bits: &str, out: &mut Vec<u8>) {
149    let len = i32::try_from(bits.len()).unwrap_or(i32::MAX);
150    out.extend_from_slice(&len.to_be_bytes());
151    out.extend_from_slice(&bit_bytes(bits));
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    /// The values and the errors of PostgreSQL 19 for the same text.
159    #[test]
160    fn text_forms() {
161        assert_eq!(bit_in("1011", -1, false).unwrap(), "1011");
162        assert_eq!(bit_in("", -1, false).unwrap(), "");
163        assert_eq!(bit_in("x1f", -1, true).unwrap(), "00011111");
164        assert_eq!(bit_in("X1F", 8, false).unwrap(), "00011111");
165        assert_eq!(bit_in("b101", -1, true).unwrap(), "101");
166        assert_eq!(bit_in("XABC", -1, false).unwrap(), "101010111100");
167        assert_eq!(
168            bit_in("102", -1, true).unwrap_err().message,
169            "\"2\" is not a valid binary digit"
170        );
171        assert_eq!(
172            bit_in("x1g", -1, true).unwrap_err().message,
173            "\"g\" is not a valid hexadecimal digit"
174        );
175        assert_eq!(bit_in("é", -1, true).unwrap_err().message, "\"é\" is not a valid binary digit");
176        let error = bit_in("101", 4, false).unwrap_err();
177        assert_eq!(
178            (error.sqlstate.as_str(), error.message.as_str()),
179            ("22026", "bit string length 3 does not match type bit(4)")
180        );
181        let error = bit_in("1010", 3, true).unwrap_err();
182        assert_eq!(
183            (error.sqlstate.as_str(), error.message.as_str()),
184            ("22001", "bit string too long for type bit varying(3)")
185        );
186        assert_eq!(bit_in("1010", 5, true).unwrap(), "1010");
187    }
188
189    #[test]
190    fn length_casts() {
191        assert_eq!(bit_coerce("101", 5, false, true).unwrap(), "10100");
192        assert_eq!(bit_coerce("10101", 3, false, true).unwrap(), "101");
193        assert_eq!(bit_coerce("101", 2, true, true).unwrap(), "10");
194        assert_eq!(bit_coerce("101", 5, true, true).unwrap(), "101");
195        assert!(bit_coerce("101", 5, false, false).is_err());
196        assert_eq!(bit_coerce("101", -1, false, false).unwrap(), "101");
197    }
198
199    #[test]
200    fn binary_forms() {
201        let mut out = Vec::new();
202        bit_send("1010101010", &mut out);
203        assert_eq!(out, [0, 0, 0, 10, 0xaa, 0x80]);
204        assert_eq!(bit_recv(&mut Recv::new(&out), -1, true).unwrap(), "1010101010");
205        let mut out = Vec::new();
206        bit_send("", &mut out);
207        assert_eq!(out, [0, 0, 0, 0]);
208        assert!(bit_recv(&mut Recv::new(&[0xff, 0xff, 0xff, 0xff]), -1, true).is_err());
209    }
210}