use crate::error::TypeError;
use crate::number::is_space;
fn strtoul10(b: &[u8]) -> (u64, usize, bool) {
let at = |i: usize| b.get(i).copied().unwrap_or(0);
let mut i = 0;
while is_space(at(i)) {
i += 1;
}
let negative = at(i) == b'-';
if negative || at(i) == b'+' {
i += 1;
}
let first = i;
let mut value = 0u64;
let mut overflow = false;
while at(i).is_ascii_digit() {
match value.checked_mul(10).and_then(|v| v.checked_add(u64::from(at(i) - b'0'))) {
Some(v) => value = v,
None => overflow = true,
}
i += 1;
}
if i == first {
return (0, 0, false);
}
(if negative { value.wrapping_neg() } else { value }, i, overflow)
}
pub fn tid_in(s: &str) -> Result<(u32, u16), TypeError> {
let syntax = || TypeError::syntax("tid", s);
let b = s.as_bytes();
let mut coords = [0usize; 2];
let mut found = 0;
for (i, &c) in b.iter().enumerate() {
if found == 2 || c == b')' {
break;
}
if c == b',' || (c == b'(' && found == 0) {
coords[found] = i + 1;
found += 1;
}
}
if found < 2 {
return Err(syntax());
}
let (block, end, overflow) = strtoul10(&b[coords[0]..]);
if overflow || b.get(coords[0] + end) != Some(&b',') {
return Err(syntax());
}
let block32 = u32::try_from(block)
.or_else(|_| i32::try_from(block.cast_signed()).map(i32::cast_unsigned))
.map_err(|_| syntax())?;
let (offset, end, overflow) = strtoul10(&b[coords[1]..]);
if overflow || b.get(coords[1] + end) != Some(&b')') {
return Err(syntax());
}
let offset = u16::try_from(offset).map_err(|_| syntax())?;
Ok((block32, offset))
}
pub fn tid_out(block: u32, offset: u16, out: &mut Vec<u8>) {
out.extend_from_slice(format!("({block},{offset})").as_bytes());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn text_forms() {
for (input, output) in [
("(0,1)", "(0,1)"),
("(4294967295,65535)", "(4294967295,65535)"),
("(-1,1)", "(4294967295,1)"),
("ab(1,2)cd", "(1,2)"),
("(,2)", "(0,2)"),
("( 1, 2)", "(1,2)"),
("(+1,+2)", "(1,2)"),
] {
let (block, offset) = tid_in(input).unwrap();
let mut out = Vec::new();
tid_out(block, offset, &mut out);
assert_eq!(String::from_utf8(out).unwrap(), output, "{input}");
}
for input in ["(1,65536)", "(4294967296,1)", "(1,2", "1,2)", "(1 ,2)"] {
assert_eq!(tid_in(input).unwrap_err().sqlstate.as_str(), "22P02", "{input}");
}
}
}