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}