// Byte-range variants of the scalar parsers: identical semantics to the
// String versions in num.mbt and date.mbt (optional sign, overflow
// checked, no trailing junk, strict yyyy-mm-dd) but reading a Bytes
// range directly, so CSV materialization never builds per-cell Strings
// for numeric, boolean or date columns.
///|
pub fn parse_i32_bytes(buf : Bytes, start : Int32, end : Int32) -> Int32? {
// fast path: short digit runs accumulate in Int32 directly (9 digits
// cap at 999999999, far below 2^31); anything else — long runs, edge
// magnitudes, non-digit junk — falls back to the int64 parser with
// its overflow model, keeping the semantics identical
let n = end - start
if n > 0 && n <= 9 {
let mut i = start
let mut neg = false
let first = buf.unsafe_get(i).to_int()
if first == 45 || first == 43 {
neg = first == 45
i += 1
}
if end - i > 0 && end - i <= 9 {
let mut acc = 0
let mut ok = true
while i < end {
let v = buf.unsafe_get(i).to_int()
if v < 48 || v > 57 {
ok = false
break
}
acc = acc * 10 + (v - 48)
i += 1
}
if ok {
return Some(if neg { -acc } else { acc })
}
return None
}
}
match parse_i64_bytes(buf, start, end) {
Some(v) =>
if v >= -2147483648L && v <= 2147483647L {
Some(v.to_int())
} else {
None
}
None => None
}
}
///|
pub fn parse_i64_bytes(buf : Bytes, start : Int32, end : Int32) -> Int64? {
let mut acc = 0L
let mut neg = false
let mut seen_sign = false
let mut digits = 0
for i in start.. 57 {
return None
}
digits += 1
// floor(2^63 / 10): past this magnitude the * 10 step would wrap
if acc < -922337203685477580L {
return None
}
acc = acc * 10L - (v - 48).to_int64()
if acc > 0L {
return None // wrapped past -2^63
}
}
if !seen_sign || digits == 0 {
return None // empty or sign-only
}
if !neg {
if acc == -9223372036854775808L {
return None // 2^63 overflows positive int64
}
acc = -acc
}
Some(acc)
}
///|
pub fn parse_f64_bytes(buf : Bytes, start : Int32, end : Int32) -> Double? {
let n = end - start
if n == 0 {
return None
}
let mut i = start
let mut neg = false
if buf[i].to_int() == 45 || buf[i].to_int() == 43 {
neg = buf[i].to_int() == 45
i += 1
}
let mut value = 0.0
let mut int_digits = 0
while i < end && is_digit_byte(buf.unsafe_get(i)) {
value = value * 10.0 + (buf.unsafe_get(i).to_int() - 48).to_double()
int_digits += 1
i += 1
}
let mut frac_div = 1.0
if i < end && buf.unsafe_get(i).to_int() == 46 {
// '.'
i += 1
while i < end && is_digit_byte(buf.unsafe_get(i)) {
frac_div = frac_div * 10.0
value = value * 10.0 + (buf.unsafe_get(i).to_int() - 48).to_double()
i += 1
}
}
if int_digits == 0 && frac_div == 1.0 {
return None
}
if frac_div > 1.0 {
value = value / frac_div
}
if i < end && (buf[i].to_int() == 101 || buf[i].to_int() == 69) {
// 'e' or 'E'
i += 1
let mut exp_neg = false
if i < end && (buf[i].to_int() == 45 || buf[i].to_int() == 43) {
exp_neg = buf[i].to_int() == 45
i += 1
}
let mut exp = 0
let mut exp_digits = 0
while i < end && is_digit_byte(buf[i]) {
exp = exp * 10 + (buf[i].to_int() - 48)
exp_digits += 1
i += 1
if exp > 300 {
return None
}
}
if exp_digits == 0 {
return None
}
if exp_neg {
value = value / pow10(exp)
} else {
value = value * pow10(exp)
}
}
if i != end {
return None // trailing junk
}
Some(if neg { -value } else { value })
}
///|
pub fn parse_bool_bytes(buf : Bytes, start : Int32, end : Int32) -> Bool? {
let n = end - start
if n == 4 &&
buf.unsafe_get(start).to_int() == 116 && // t
buf.unsafe_get(start + 1).to_int() == 114 && // r
buf.unsafe_get(start + 2).to_int() == 117 && // u
buf.unsafe_get(start + 3).to_int() == 101 {
Some(true)
} else if n == 5 &&
buf.unsafe_get(start).to_int() == 102 && // f
buf.unsafe_get(start + 1).to_int() == 97 && // a
buf.unsafe_get(start + 2).to_int() == 108 && // l
buf.unsafe_get(start + 3).to_int() == 115 && // s
buf.unsafe_get(start + 4).to_int() == 101 {
Some(false)
} else {
None
}
}
///|
/// Strict ISO "yyyy-mm-dd" over a byte range, mirroring parse_date.
pub fn parse_date_bytes(buf : Bytes, start : Int32, end : Int32) -> Int32? {
if end - start != 10 ||
buf.unsafe_get(start + 4).to_int() != 45 ||
buf.unsafe_get(start + 7).to_int() != 45 {
return None
}
let y = match digits4(buf, start) {
Some(v) => v
None => return None
}
let m = match digits2(buf, start + 5) {
Some(v) => v
None => return None
}
let d = match digits2(buf, start + 8) {
Some(v) => v
None => return None
}
if m < 1 || m > 12 {
return None
}
if d < 1 || d > days_in_month(y, m) {
return None
}
Some(days_from_civil(y, m, d))
}
///|
fn is_digit_byte(b : Byte) -> Bool {
let v = b.to_int()
v >= 48 && v <= 57
}
///|
fn digits4(buf : Bytes, off : Int32) -> Int32? {
match
(
digit_byte(buf.unsafe_get(off)),
digit_byte(buf.unsafe_get(off + 1)),
digit_byte(buf.unsafe_get(off + 2)),
digit_byte(buf.unsafe_get(off + 3)),
) {
(Some(a), Some(b), Some(c), Some(d)) =>
Some(a * 1000 + b * 100 + c * 10 + d)
_ => None
}
}
///|
fn digits2(buf : Bytes, off : Int32) -> Int32? {
match (digit_byte(buf.unsafe_get(off)), digit_byte(buf.unsafe_get(off + 1))) {
(Some(a), Some(b)) => Some(a * 10 + b)
_ => None
}
}
///|
fn digit_byte(b : Byte) -> Int32? {
let v = b.to_int()
if v >= 48 && v <= 57 {
Some(v - 48)
} else {
None
}
}