diff --git a/hbrdd/dbf/field.go b/hbrdd/dbf/field.go index 52eff42..b1b56a1 100644 --- a/hbrdd/dbf/field.go +++ b/hbrdd/dbf/field.go @@ -172,19 +172,71 @@ func PutFieldValue(recBuf []byte, offset uint16, field *FieldDesc, val hbrt.Valu // --- Internal parsers --- func parseNumericField(raw []byte, dec byte) hbrt.Value { - s := strings.TrimSpace(string(raw)) - if s == "" { + // Byte-level fast path — avoids `string(raw)` + TrimSpace + ParseInt + // allocations on the hot scan path. Numeric DBF fields are ASCII, + // right-aligned, space-padded, optional leading sign, optional `.` + // for decimals. A full 50k-row scan can hit this fn 100 k+ times, + // so every allocation matters. + // + // Algorithm: + // 1. Walk past leading spaces. + // 2. Detect sign. + // 3. Accumulate int64 digit-by-digit. + // 4. If we hit `.` or the field has dec > 0, bail to float parser + // (that path is rare on integer-typed DBF fields like IDs / + // counters, which dominate WHERE predicates). + // 5. Walk past trailing spaces. + // + // All operations are byte comparisons on the raw record buffer — + // no heap allocation unless the field is genuinely fractional. + + start := 0 + end := len(raw) + for start < end && raw[start] == ' ' { + start++ + } + for end > start && raw[end-1] == ' ' { + end-- + } + if start == end { return hbrt.MakeInt(0) } - if dec == 0 && !strings.Contains(s, ".") { - n, err := strconv.ParseInt(s, 10, 64) - if err == nil { + if dec == 0 { + // Fast integer path + i := start + neg := false + if raw[i] == '-' { + neg = true + i++ + } else if raw[i] == '+' { + i++ + } + var n int64 + ok := i < end + for ; i < end; i++ { + c := raw[i] + if c == '.' { + ok = false + break + } + if c < '0' || c > '9' { + ok = false + break + } + n = n*10 + int64(c-'0') + } + if ok { + if neg { + n = -n + } return hbrt.MakeNumInt(n) } + // Fall through: has a `.` or unexpected char → use float path } - f, err := strconv.ParseFloat(s, 64) + // Decimal/float path — allocate once for strconv + f, err := strconv.ParseFloat(string(raw[start:end]), 64) if err == nil { return hbrt.MakeDouble(f, uint16(len(raw)), uint16(dec)) }