master
  1//! A wrapper over a byte-slice, providing useful methods for parsing string floating point values.
  2
  3const std = @import("std");
  4const FloatStream = @This();
  5const common = @import("common.zig");
  6
  7slice: []const u8,
  8offset: usize,
  9underscore_count: usize,
 10
 11pub fn init(s: []const u8) FloatStream {
 12    return .{ .slice = s, .offset = 0, .underscore_count = 0 };
 13}
 14
 15// Returns the offset from the start *excluding* any underscores that were found.
 16pub fn offsetTrue(self: FloatStream) usize {
 17    return self.offset - self.underscore_count;
 18}
 19
 20pub fn reset(self: *FloatStream) void {
 21    self.offset = 0;
 22    self.underscore_count = 0;
 23}
 24
 25pub fn len(self: FloatStream) usize {
 26    if (self.offset > self.slice.len) {
 27        return 0;
 28    }
 29    return self.slice.len - self.offset;
 30}
 31
 32pub fn hasLen(self: FloatStream, n: usize) bool {
 33    return self.offset + n <= self.slice.len;
 34}
 35
 36pub fn firstUnchecked(self: FloatStream) u8 {
 37    return self.slice[self.offset];
 38}
 39
 40pub fn first(self: FloatStream) ?u8 {
 41    return if (self.hasLen(1))
 42        return self.firstUnchecked()
 43    else
 44        null;
 45}
 46
 47pub fn isEmpty(self: FloatStream) bool {
 48    return !self.hasLen(1);
 49}
 50
 51pub fn firstIs(self: FloatStream, comptime cs: []const u8) bool {
 52    if (self.first()) |ok| {
 53        inline for (cs) |c| if (ok == c) return true;
 54    }
 55    return false;
 56}
 57
 58pub fn firstIsLower(self: FloatStream, comptime cs: []const u8) bool {
 59    if (self.first()) |ok| {
 60        inline for (cs) |c| if (ok | 0x20 == c) return true;
 61    }
 62    return false;
 63}
 64
 65pub fn firstIsDigit(self: FloatStream, comptime base: u8) bool {
 66    comptime std.debug.assert(base == 10 or base == 16);
 67
 68    if (self.first()) |ok| {
 69        return common.isDigit(ok, base);
 70    }
 71    return false;
 72}
 73
 74pub fn advance(self: *FloatStream, n: usize) void {
 75    self.offset += n;
 76}
 77
 78pub fn skipChars(self: *FloatStream, comptime cs: []const u8) void {
 79    while (self.firstIs(cs)) : (self.advance(1)) {}
 80}
 81
 82pub fn readU64Unchecked(self: FloatStream) u64 {
 83    return std.mem.readInt(u64, self.slice[self.offset..][0..8], .little);
 84}
 85
 86pub fn readU64(self: FloatStream) ?u64 {
 87    if (self.hasLen(8)) {
 88        return self.readU64Unchecked();
 89    }
 90    return null;
 91}
 92
 93pub fn atUnchecked(self: *FloatStream, i: usize) u8 {
 94    return self.slice[self.offset + i];
 95}
 96
 97pub fn scanDigit(self: *FloatStream, comptime base: u8) ?u8 {
 98    comptime std.debug.assert(base == 10 or base == 16);
 99
100    retry: while (true) {
101        if (self.first()) |ok| {
102            if ('0' <= ok and ok <= '9') {
103                self.advance(1);
104                return ok - '0';
105            } else if (base == 16 and 'a' <= ok and ok <= 'f') {
106                self.advance(1);
107                return ok - 'a' + 10;
108            } else if (base == 16 and 'A' <= ok and ok <= 'F') {
109                self.advance(1);
110                return ok - 'A' + 10;
111            } else if (ok == '_') {
112                self.advance(1);
113                self.underscore_count += 1;
114                continue :retry;
115            }
116        }
117        return null;
118    }
119}