1use crate::error::TypeError;
8
9pub(crate) fn is_space(b: u8) -> bool {
11 matches!(b, b' ' | b'\t' | b'\n' | 0x0b | 0x0c | b'\r')
12}
13
14pub(crate) fn digit(b: u8, base: u64) -> Option<u64> {
16 let value = match b {
17 b'0'..=b'9' => b - b'0',
18 b'a'..=b'f' => b - b'a' + 10,
19 b'A'..=b'F' => b - b'A' + 10,
20 _ => return None,
21 };
22 (u64::from(value) < base).then_some(u64::from(value))
23}
24
25fn parse_int(s: &str, limit: u64, type_name: &str) -> Result<i128, TypeError> {
27 let b = s.as_bytes();
28 let at = |i: usize| b.get(i).copied().unwrap_or(0);
29 let range = || TypeError::range(s, type_name);
30 let syntax = || TypeError::syntax(type_name, s);
31 let finish = |tmp: u64, neg: bool| match neg {
32 true if tmp > limit => Err(range()),
33 true => Ok(-i128::from(tmp)),
34 false if tmp >= limit => Err(range()),
35 false => Ok(i128::from(tmp)),
36 };
37
38 let mut i = usize::from(at(0) == b'-');
39 if at(i).is_ascii_digit() {
40 let mut tmp = 0u64;
41 while at(i).is_ascii_digit() {
42 if tmp > limit / 10 {
43 return Err(range());
44 }
45 tmp = tmp * 10 + u64::from(at(i) - b'0');
46 i += 1;
47 }
48 if i == b.len() {
49 return finish(tmp, at(0) == b'-');
50 }
51 }
52
53 i = 0;
54 while is_space(at(i)) {
55 i += 1;
56 }
57 let neg = at(i) == b'-';
58 if neg || at(i) == b'+' {
59 i += 1;
60 }
61 let base = match (at(i), at(i + 1)) {
62 (b'0', b'x' | b'X') => 16,
63 (b'0', b'o' | b'O') => 8,
64 (b'0', b'b' | b'B') => 2,
65 _ => 10,
66 };
67 if base != 10 {
68 i += 2;
69 }
70 let first = i;
71 let mut tmp = 0u64;
72 loop {
73 if let Some(d) = digit(at(i), base) {
74 if tmp > limit / base {
75 return Err(range());
76 }
77 tmp = tmp * base + d;
78 i += 1;
79 } else if at(i) == b'_' {
80 if base == 10 && i == first {
82 return Err(syntax());
83 }
84 i += 1;
85 if digit(at(i), base).is_none() {
86 return Err(syntax());
87 }
88 } else {
89 break;
90 }
91 }
92 if i == first {
93 return Err(syntax());
94 }
95 while is_space(at(i)) {
96 i += 1;
97 }
98 if i != b.len() {
99 return Err(syntax());
100 }
101 finish(tmp, neg)
102}
103
104pub fn int2_in(s: &str) -> Result<i16, TypeError> {
106 parse_int(s, 1 << 15, "smallint").map(|v| v as i16)
107}
108
109pub fn int4_in(s: &str) -> Result<i32, TypeError> {
111 parse_int(s, 1 << 31, "integer").map(|v| v as i32)
112}
113
114pub fn int8_in(s: &str) -> Result<i64, TypeError> {
116 parse_int(s, 1 << 63, "bigint").map(|v| v as i64)
117}
118
119fn strtoul(b: &[u8]) -> (u64, usize, bool) {
121 let at = |i: usize| b.get(i).copied().unwrap_or(0);
122 let mut i = 0;
123 while is_space(at(i)) {
124 i += 1;
125 }
126 let neg = at(i) == b'-';
127 if neg || at(i) == b'+' {
128 i += 1;
129 }
130 let base = match (at(i), at(i + 1)) {
132 (b'0', b'x' | b'X') if digit(at(i + 2), 16).is_some() => {
133 i += 2;
134 16
135 }
136 (b'0', _) => 8,
137 _ => 10,
138 };
139 let first = i;
140 let mut value = 0u64;
141 let mut overflow = false;
142 while let Some(d) = digit(at(i), base) {
143 match value.checked_mul(base).and_then(|v| v.checked_add(d)) {
144 Some(v) => value = v,
145 None => overflow = true,
146 }
147 i += 1;
148 }
149 if i == first {
150 return (0, 0, false);
151 }
152 match (overflow, neg) {
153 (true, _) => (u64::MAX, i, true),
154 (false, true) => (value.wrapping_neg(), i, false),
155 (false, false) => (value, i, false),
156 }
157}
158
159pub fn oid_in(s: &str) -> Result<u32, TypeError> {
161 uint32_in(s, "oid")
162}
163
164pub fn uint32_in(s: &str, type_name: &str) -> Result<u32, TypeError> {
166 let b = s.as_bytes();
167 let (value, mut end, overflow) = strtoul(b);
168 if end == 0 {
169 return Err(TypeError::syntax(type_name, s));
170 }
171 if overflow {
172 return Err(TypeError::range(s, type_name));
173 }
174 while end < b.len() && is_space(b[end]) {
175 end += 1;
176 }
177 if end != b.len() {
178 return Err(TypeError::syntax(type_name, s));
179 }
180 let result = value as u32;
181 if value != u64::from(result) && value != i64::from(result as i32) as u64 {
182 return Err(TypeError::range(s, type_name));
183 }
184 Ok(result)
185}
186
187pub fn uint64_in(s: &str, type_name: &str) -> Result<u64, TypeError> {
193 let b = s.as_bytes();
194 let (value, mut end, overflow) = strtoul(b);
195 if end == 0 {
196 return Err(TypeError::syntax(type_name, s));
197 }
198 if overflow {
199 return Err(TypeError::range(s, type_name));
200 }
201 while end < b.len() && is_space(b[end]) {
202 end += 1;
203 }
204 if end != b.len() {
205 return Err(TypeError::syntax(type_name, s));
206 }
207 Ok(value)
208}
209
210pub(crate) fn uint32_in_subr(s: &str, type_name: &str) -> Result<(u32, usize), TypeError> {
212 let (value, end, overflow) = strtoul(s.as_bytes());
213 if end == 0 {
214 return Err(TypeError::syntax(type_name, s));
215 }
216 let result = value as u32;
217 if overflow || (value != u64::from(result) && value != i64::from(result as i32) as u64) {
218 return Err(TypeError::range(s, type_name));
219 }
220 Ok((result, end))
221}
222
223const PAIRS: &[u8; 200] = b"00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
224
225pub fn u64_out(mut n: u64, out: &mut Vec<u8>) {
227 let mut buf = [0u8; 20];
228 let mut at = buf.len();
229 while n >= 100 {
230 let pair = (n % 100) as usize * 2;
231 n /= 100;
232 at -= 2;
233 buf[at..at + 2].copy_from_slice(&PAIRS[pair..pair + 2]);
234 }
235 if n >= 10 {
236 let pair = n as usize * 2;
237 at -= 2;
238 buf[at..at + 2].copy_from_slice(&PAIRS[pair..pair + 2]);
239 } else {
240 at -= 1;
241 buf[at] = b'0' + n as u8;
242 }
243 out.extend_from_slice(&buf[at..]);
244}
245
246pub fn int_out(n: i64, out: &mut Vec<u8>) {
248 if n < 0 {
249 out.push(b'-');
250 }
251 u64_out(n.unsigned_abs(), out);
252}
253
254pub fn oid_out(n: u32, out: &mut Vec<u8>) {
256 u64_out(u64::from(n), out);
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 fn text(f: impl Fn(&mut Vec<u8>)) -> String {
264 let mut out = Vec::new();
265 f(&mut out);
266 String::from_utf8(out).unwrap()
267 }
268
269 #[test]
270 fn integers_take_the_forms_of_numutils() {
271 for (input, value) in [
272 ("0", 0),
273 ("-0", 0),
274 (" 12 ", 12),
275 ("+7", 7),
276 ("0x1F", 31),
277 ("-0X1f", -31),
278 ("0o17", 15),
279 ("0b101", 5),
280 ("1_000", 1000),
281 ("0x_1F", 31),
282 ("2147483647", i32::MAX),
283 ("-2147483648", i32::MIN),
284 ("-0x80000000", i32::MIN),
285 ("\t\n42\r", 42),
286 ] {
287 assert_eq!(int4_in(input), Ok(value), "{input:?}");
288 }
289 for input in ["", " ", "1e3", "1.5", "_1", "1_", "1__0", "0x", "0b2", "- 1", "1 2", "0o8"] {
290 let error = int4_in(input).unwrap_err();
291 assert_eq!(error.sqlstate.as_str(), "22P02", "{input:?}");
292 assert_eq!(
293 error.message,
294 format!("invalid input syntax for type integer: \"{input}\"")
295 );
296 }
297 for input in ["2147483648", "-2147483649", "0x80000000", " 99999999999 ", "99999999999x"] {
298 let error = int4_in(input).unwrap_err();
299 assert_eq!(error.sqlstate.as_str(), "22003", "{input:?}");
300 assert_eq!(
301 error.message,
302 format!("value \"{input}\" is out of range for type integer")
303 );
304 }
305 assert_eq!(int2_in("-32768"), Ok(i16::MIN));
306 assert_eq!(
307 int2_in("32768").unwrap_err().message,
308 "value \"32768\" is out of range for type smallint"
309 );
310 assert_eq!(int8_in("-9223372036854775808"), Ok(i64::MIN));
311 assert_eq!(int8_in("0x7FFF_FFFF_FFFF_FFFF"), Ok(i64::MAX));
312 assert_eq!(
313 int8_in("x").unwrap_err().message,
314 "invalid input syntax for type bigint: \"x\""
315 );
316 }
317
318 #[test]
319 fn an_oid_takes_what_strtoul_takes() {
320 for (input, value) in [
321 ("0", 0),
322 ("16384", 16384),
323 (" 010 ", 8),
324 ("0x10", 16),
325 ("-1", u32::MAX),
326 ("-2147483648", 1 << 31),
327 ("4294967295", u32::MAX),
328 ("+5", 5),
329 ] {
330 assert_eq!(oid_in(input), Ok(value), "{input:?}");
331 }
332 for input in ["", "x", "0x", "08", "1_0", "1.0", "-"] {
333 assert_eq!(oid_in(input).unwrap_err().sqlstate.as_str(), "22P02", "{input:?}");
334 }
335 for input in ["4294967296", "-2147483649", "99999999999999999999999"] {
336 assert_eq!(
337 oid_in(input).unwrap_err().message,
338 format!("value \"{input}\" is out of range for type oid")
339 );
340 }
341 }
342
343 #[test]
344 fn integers_print_as_digits() {
345 for n in [0, 7, -7, 10, 99, 100, 12345, i64::MAX, i64::MIN, -1_000_000] {
346 assert_eq!(text(|out| int_out(n, out)), n.to_string());
347 }
348 assert_eq!(text(|out| oid_out(u32::MAX, out)), "4294967295");
349 }
350}