authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-03-09 19:12:15-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-03-09 19:12:15-05:00
log47f267d25fcf1a85b983e6f059c18e6163324897
treefce0b77e7da97db47ad14f8d7a81ad855d30e226
parentc62db5721c1bf3a4f5a469c7ae0ded7c84008c81

break off some of std.io into std.fmt, generalize printf

closes #250

6 files changed, 378 insertions(+), 295 deletions(-)

CMakeLists.txt+1
...@@ -210,6 +210,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/elf.zig" DESTINATION "${ZIG_STD_DEST}")...@@ -210,6 +210,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/elf.zig" DESTINATION "${ZIG_STD_DEST}")
210install(FILES "${CMAKE_SOURCE_DIR}/std/empty.zig" DESTINATION "${ZIG_STD_DEST}")210install(FILES "${CMAKE_SOURCE_DIR}/std/empty.zig" DESTINATION "${ZIG_STD_DEST}")
211install(FILES "${CMAKE_SOURCE_DIR}/std/endian.zig" DESTINATION "${ZIG_STD_DEST}")211install(FILES "${CMAKE_SOURCE_DIR}/std/endian.zig" DESTINATION "${ZIG_STD_DEST}")
212install(FILES "${CMAKE_SOURCE_DIR}/std/errno.zig" DESTINATION "${ZIG_STD_DEST}")212install(FILES "${CMAKE_SOURCE_DIR}/std/errno.zig" DESTINATION "${ZIG_STD_DEST}")
213install(FILES "${CMAKE_SOURCE_DIR}/std/fmt.zig" DESTINATION "${ZIG_STD_DEST}")
213install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}")214install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}")
214install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")215install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")
215install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")216install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")
example/guess_number/main.zig+2-1
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const io = std.io;2const io = std.io;
3const fmt = std.fmt;
3const Rand = std.rand.Rand;4const Rand = std.rand.Rand;
4const os = std.os;5const os = std.os;
56
...@@ -23,7 +24,7 @@ pub fn main(args: [][]u8) -> %void {...@@ -23,7 +24,7 @@ pub fn main(args: [][]u8) -> %void {
23 return err;24 return err;
24 };25 };
2526
26 const guess = io.parseUnsigned(u8, line_buf[0...line_len - 1], 10) %% {27 const guess = fmt.parseUnsigned(u8, line_buf[0...line_len - 1], 10) %% {
27 %%io.stdout.printf("Invalid number.\n");28 %%io.stdout.printf("Invalid number.\n");
28 continue;29 continue;
29 };30 };
std/fmt.zig created+334
...@@ -0,0 +1,334 @@
1const math = @import("math.zig");
2const debug = @import("debug.zig");
3const assert = debug.assert;
4const mem = @import("mem.zig");
5
6const max_f64_digits = 65;
7const max_int_digits = 65;
8
9const State = enum { // TODO put inside format function and make sure the name and debug info is correct
10 Start,
11 OpenBrace,
12 CloseBrace,
13 Integer,
14 IntegerWidth,
15 Character,
16};
17
18/// Renders fmt string with args, calling output with slices of bytes.
19/// Return false from output function and output will not be called again.
20/// Returns false if output ever returned false, true otherwise.
21pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
22 comptime fmt: []const u8, args: ...) -> bool
23{
24 comptime var start_index = 0;
25 comptime var state = State.Start;
26 comptime var next_arg = 0;
27 comptime var radix = 0;
28 comptime var uppercase = false;
29 comptime var width = 0;
30 comptime var width_start = 0;
31
32 inline for (fmt) |c, i| {
33 switch (state) {
34 State.Start => switch (c) {
35 '{' => {
36 // TODO if you make this an if statement with && then it breaks
37 if (start_index < i) {
38 if (!output(context, fmt[start_index...i]))
39 return false;
40 }
41 state = State.OpenBrace;
42 },
43 '}' => {
44 if (start_index < i) {
45 if (!output(context, fmt[start_index...i]))
46 return false;
47 }
48 state = State.CloseBrace;
49 },
50 else => {},
51 },
52 State.OpenBrace => switch (c) {
53 '{' => {
54 state = State.Start;
55 start_index = i;
56 },
57 '}' => {
58 if (!formatValue(args[next_arg], context, output))
59 return false;
60 next_arg += 1;
61 state = State.Start;
62 start_index = i + 1;
63 },
64 'd' => {
65 radix = 10;
66 uppercase = false;
67 width = 0;
68 state = State.Integer;
69 },
70 'x' => {
71 radix = 16;
72 uppercase = false;
73 width = 0;
74 state = State.Integer;
75 },
76 'X' => {
77 radix = 16;
78 uppercase = true;
79 width = 0;
80 state = State.Integer;
81 },
82 'c' => {
83 state = State.Character;
84 },
85 else => @compileError("Unknown format character: " ++ []u8{c}),
86 },
87 State.CloseBrace => switch (c) {
88 '}' => {
89 state = State.Start;
90 start_index = i;
91 },
92 else => @compileError("Single '}' encountered in format string"),
93 },
94 State.Integer => switch (c) {
95 '}' => {
96 if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
97 return false;
98 next_arg += 1;
99 state = State.Start;
100 start_index = i + 1;
101 },
102 '0' ... '9' => {
103 width_start = i;
104 state = State.IntegerWidth;
105 },
106 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
107 },
108 State.IntegerWidth => switch (c) {
109 '}' => {
110 width = comptime %%parseUnsigned(usize, fmt[width_start...i], 10);
111 if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
112 return false;
113 next_arg += 1;
114 state = State.Start;
115 start_index = i + 1;
116 },
117 '0' ... '9' => {},
118 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
119 },
120 State.Character => switch (c) {
121 '}' => {
122 if (!formatAsciiChar(args[next_arg], context, output))
123 return false;
124 next_arg += 1;
125 state = State.Start;
126 start_index = i + 1;
127 },
128 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
129 },
130 }
131 }
132 comptime {
133 if (args.len != next_arg) {
134 @compileError("Unused arguments");
135 }
136 if (state != State.Start) {
137 @compileError("Incomplete format string: " ++ fmt);
138 }
139 }
140 if (start_index < fmt.len) {
141 if (!output(context, fmt[start_index...]))
142 return false;
143 }
144
145 return true;
146}
147
148pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
149 const T = @typeOf(value);
150 if (@isInteger(T)) {
151 return formatInt(value, 10, false, 0, context, output);
152 } else if (@isFloat(T)) {
153 @compileError("TODO implement formatFloat");
154 } else if (@canImplicitCast([]const u8, value)) {
155 const casted_value = ([]const u8)(value);
156 return output(context, casted_value);
157 } else if (T == void) {
158 return output(context, "void");
159 } else {
160 @compileError("Unable to format type '" ++ @typeName(T) ++ "'");
161 }
162}
163
164pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
165 return output(context, (&c)[0...1]);
166}
167
168pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
169 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
170{
171 if (@typeOf(value).is_signed) {
172 return formatIntSigned(value, base, uppercase, width, context, output);
173 } else {
174 return formatIntUnsigned(value, base, uppercase, width, context, output);
175 }
176}
177
178fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
179 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
180{
181 const uint = @intType(false, @typeOf(value).bit_count);
182 if (value < 0) {
183 const minus_sign: u8 = '-';
184 if (!output(context, (&minus_sign)[0...1]))
185 return false;
186 const new_value = uint(-(value + 1)) + 1;
187 const new_width = if (width == 0) 0 else (width - 1);
188 return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
189 } else if (width == 0) {
190 return formatIntUnsigned(uint(value), base, uppercase, width, context, output);
191 } else {
192 const plus_sign: u8 = '+';
193 if (!output(context, (&plus_sign)[0...1]))
194 return false;
195 const new_value = uint(value);
196 const new_width = if (width == 0) 0 else (width - 1);
197 return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
198 }
199}
200
201fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
202 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
203{
204 // max_int_digits accounts for the minus sign. when printing an unsigned
205 // number we don't need to do that.
206 var buf: [max_int_digits - 1]u8 = undefined;
207 var a = value;
208 var index: usize = buf.len;
209
210 while (true) {
211 const digit = a % base;
212 index -= 1;
213 buf[index] = digitToChar(u8(digit), uppercase);
214 a /= base;
215 if (a == 0)
216 break;
217 }
218
219 const digits_buf = buf[index...];
220 const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;
221
222 if (padding > index) {
223 const zero_byte: u8 = '0';
224 var leftover_padding = padding - index;
225 while (true) {
226 if (!output(context, (&zero_byte)[0...1]))
227 return false;
228 leftover_padding -= 1;
229 if (leftover_padding == 0)
230 break;
231 }
232 mem.set(u8, buf[0...index], '0');
233 return output(context, buf);
234 } else {
235 const padded_buf = buf[index - padding...];
236 mem.set(u8, padded_buf[0...padding], '0');
237 return output(context, padded_buf);
238 }
239}
240
241pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> usize {
242 var context = FormatIntBuf {
243 .out_buf = out_buf,
244 .index = 0,
245 };
246 _ = formatInt(value, base, uppercase, width, &context, formatIntCallback);
247 return context.index;
248}
249const FormatIntBuf = struct {
250 out_buf: []u8,
251 index: usize,
252};
253fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> bool {
254 mem.copy(u8, context.out_buf[context.index...], bytes);
255 context.index += bytes.len;
256 return true;
257}
258
259pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
260 var x: T = 0;
261
262 for (buf) |c| {
263 const digit = %return charToDigit(c, radix);
264 x = %return math.mulOverflow(T, x, radix);
265 x = %return math.addOverflow(T, x, digit);
266 }
267
268 return x;
269}
270
271error InvalidChar;
272fn charToDigit(c: u8, radix: u8) -> %u8 {
273 const value = switch (c) {
274 '0' ... '9' => c - '0',
275 'A' ... 'Z' => c - 'A' + 10,
276 'a' ... 'z' => c - 'a' + 10,
277 else => return error.InvalidChar,
278 };
279
280 if (value >= radix)
281 return error.InvalidChar;
282
283 return value;
284}
285
286fn digitToChar(digit: u8, uppercase: bool) -> u8 {
287 return switch (digit) {
288 0 ... 9 => digit + '0',
289 10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10),
290 else => @unreachable(),
291 };
292}
293
294fn testBufPrintInt() {
295 @setFnTest(this);
296
297 var buffer: [max_int_digits]u8 = undefined;
298 const buf = buffer[0...];
299 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
300 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));
301 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));
302 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));
303
304 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));
305
306 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));
307 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));
308 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));
309
310 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));
311 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));
312}
313
314fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 {
315 return buf[0...formatIntBuf(buf, value, base, uppercase, width)];
316}
317
318fn testParseU64DigitTooBig() {
319 @setFnTest(this);
320
321 parseUnsigned(u64, "123a", 10) %% |err| {
322 if (err == error.InvalidChar) return;
323 @unreachable();
324 };
325 @unreachable();
326}
327
328fn testParseUnsignedComptime() {
329 @setFnTest(this);
330
331 comptime {
332 assert(%%parseUnsigned(usize, "2", 10) == 2);
333 }
334}
std/index.zig+12-11
...@@ -1,20 +1,21 @@...@@ -1,20 +1,21 @@
1pub const rand = @import("rand.zig");
2pub const io = @import("io.zig");
3pub const os = @import("os.zig");
4pub const math = @import("math.zig");
5pub const cstr = @import("cstr.zig");1pub const cstr = @import("cstr.zig");
6pub const sort = @import("sort.zig");2pub const debug = @import("debug.zig");
7pub const net = @import("net.zig");3pub const fmt = @import("fmt.zig");
8pub const list = @import("list.zig");
9pub const hash_map = @import("hash_map.zig");4pub const hash_map = @import("hash_map.zig");
5pub const io = @import("io.zig");
6pub const list = @import("list.zig");
7pub const math = @import("math.zig");
10pub const mem = @import("mem.zig");8pub const mem = @import("mem.zig");
11pub const debug = @import("debug.zig");9pub const net = @import("net.zig");
10pub const os = @import("os.zig");
11pub const rand = @import("rand.zig");
12pub const sort = @import("sort.zig");
12pub const linux = switch(@compileVar("os")) {13pub const linux = switch(@compileVar("os")) {
13 Os.linux => @import("linux.zig"),14 Os.linux => @import("linux.zig"),
14 else => null_import,15 else => empty_import,
15};16};
16pub const darwin = switch(@compileVar("os")) {17pub const darwin = switch(@compileVar("os")) {
17 Os.darwin => @import("darwin.zig"),18 Os.darwin => @import("darwin.zig"),
18 else => null_import,19 else => empty_import,
19};20};
20const null_import = @import("empty.zig");21pub const empty_import = @import("empty.zig");
std/io.zig+26-280
...@@ -11,6 +11,7 @@ const assert = debug.assert;...@@ -11,6 +11,7 @@ const assert = debug.assert;
11const os = @import("os.zig");11const os = @import("os.zig");
12const mem = @import("mem.zig");12const mem = @import("mem.zig");
13const Buffer0 = @import("cstr.zig").Buffer0;13const Buffer0 = @import("cstr.zig").Buffer0;
14const fmt = @import("fmt.zig");
1415
15pub const stdin_fileno = 0;16pub const stdin_fileno = 0;
16pub const stdout_fileno = 1;17pub const stdout_fileno = 1;
...@@ -61,8 +62,6 @@ error Unseekable;...@@ -61,8 +62,6 @@ error Unseekable;
61error Eof;62error Eof;
6263
63const buffer_size = 4 * 1024;64const buffer_size = 4 * 1024;
64const max_f64_digits = 65;
65const max_int_digits = 65;
6665
67pub const OpenRead = 0b0001;66pub const OpenRead = 0b0001;
68pub const OpenWrite = 0b0010;67pub const OpenWrite = 0b0010;
...@@ -81,173 +80,46 @@ pub const OutStream = struct {...@@ -81,173 +80,46 @@ pub const OutStream = struct {
81 }80 }
8281
83 pub fn write(self: &OutStream, bytes: []const u8) -> %void {82 pub fn write(self: &OutStream, bytes: []const u8) -> %void {
84 var src_bytes_left = bytes.len;
85 var src_index: usize = 0;83 var src_index: usize = 0;
86 const dest_space_left = self.buffer.len - self.index;
8784
88 while (src_bytes_left > 0) {85 while (src_index < bytes.len) {
89 const copy_amt = math.min(dest_space_left, src_bytes_left);86 const dest_space_left = self.buffer.len - self.index;
90 @memcpy(&self.buffer[self.index], &bytes[src_index], copy_amt);87 const copy_amt = math.min(dest_space_left, bytes.len - src_index);
88 mem.copy(u8, self.buffer[self.index...], bytes[src_index...src_index + copy_amt]);
91 self.index += copy_amt;89 self.index += copy_amt;
90 assert(self.index <= self.buffer.len);
92 if (self.index == self.buffer.len) {91 if (self.index == self.buffer.len) {
93 %return self.flush();92 %return self.flush();
94 }93 }
95 src_bytes_left -= copy_amt;94 src_index += copy_amt;
96 }95 }
97 }96 }
9897
99 const State = enum { // TODO put inside printf function and make sure the name and debug info is correct
100 Start,
101 OpenBrace,
102 CloseBrace,
103 Integer,
104 IntegerWidth,
105 Character,
106 };
107
108 /// Calls print and then flushes the buffer.98 /// Calls print and then flushes the buffer.
109 pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {99 pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
110 comptime var start_index = 0;100 %return self.print(format, args);
111 comptime var state = State.Start;
112 comptime var next_arg = 0;
113 comptime var radix = 0;
114 comptime var uppercase = false;
115 comptime var width = 0;
116 comptime var width_start = 0;
117
118 inline for (format) |c, i| {
119 switch (state) {
120 State.Start => switch (c) {
121 '{' => {
122 if (start_index < i) %return self.write(format[start_index...i]);
123 state = State.OpenBrace;
124 },
125 '}' => {
126 if (start_index < i) %return self.write(format[start_index...i]);
127 state = State.CloseBrace;
128 },
129 else => {},
130 },
131 State.OpenBrace => switch (c) {
132 '{' => {
133 state = State.Start;
134 start_index = i;
135 },
136 '}' => {
137 %return self.printValue(args[next_arg]);
138 next_arg += 1;
139 state = State.Start;
140 start_index = i + 1;
141 },
142 'd' => {
143 radix = 10;
144 uppercase = false;
145 width = 0;
146 state = State.Integer;
147 },
148 'x' => {
149 radix = 16;
150 uppercase = false;
151 width = 0;
152 state = State.Integer;
153 },
154 'X' => {
155 radix = 16;
156 uppercase = true;
157 width = 0;
158 state = State.Integer;
159 },
160 'c' => {
161 state = State.Character;
162 },
163 else => @compileError("Unknown format character: " ++ []u8{c}),
164 },
165 State.CloseBrace => switch (c) {
166 '}' => {
167 state = State.Start;
168 start_index = i;
169 },
170 else => @compileError("Single '}' encountered in format string"),
171 },
172 State.Integer => switch (c) {
173 '}' => {
174 %return self.printInt(args[next_arg], radix, uppercase, width);
175 next_arg += 1;
176 state = State.Start;
177 start_index = i + 1;
178 },
179 '0' ... '9' => {
180 width_start = i;
181 state = State.IntegerWidth;
182 },
183 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
184 },
185 State.IntegerWidth => switch (c) {
186 '}' => {
187 width = comptime %%parseUnsigned(usize, format[width_start...i], 10);
188 %return self.printInt(args[next_arg], radix, uppercase, width);
189 next_arg += 1;
190 state = State.Start;
191 start_index = i + 1;
192 },
193 '0' ... '9' => {},
194 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
195 },
196 State.Character => switch (c) {
197 '}' => {
198 %return self.printAsciiChar(args[next_arg]);
199 next_arg += 1;
200 state = State.Start;
201 start_index = i + 1;
202 },
203 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
204 },
205 }
206 }
207 comptime {
208 if (args.len != next_arg) {
209 @compileError("Unused arguments");
210 }
211 if (state != State.Start) {
212 @compileError("Incomplete format string: " ++ format);
213 }
214 }
215 if (start_index < format.len) {
216 %return self.write(format[start_index...format.len]);
217 }
218 %return self.flush();101 %return self.flush();
219 }102 }
220103
221 pub fn printValue(self: &OutStream, value: var) -> %void {104 /// Does not flush the buffer.
222 const T = @typeOf(value);105 pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
223 if (@isInteger(T)) {106 var context = PrintContext {
224 return self.printInt(value, 10, false, 0);107 .self = self,
225 } else if (@isFloat(T)) {108 .result = {},
226 return self.printFloat(T, value);109 };
227 } else if (@canImplicitCast([]const u8, value)) {110 _ = fmt.format(&context, printOutput, format, args);
228 const casted_value = ([]const u8)(value);111 return context.result;
229 return self.write(casted_value);
230 } else if (T == void) {
231 return self.write("void");
232 } else {
233 @compileError("Unable to print type '" ++ @typeName(T) ++ "'");
234 }
235 }112 }
236113 const PrintContext = struct {
237 pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void {114 self: &OutStream,
238 if (self.index + max_int_digits >= self.buffer.len) {115 result: %void,
239 %return self.flush();116 };
240 }117 fn printOutput(context: &PrintContext, bytes: []const u8) -> bool {
241 const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width);118 context.self.write(bytes) %% |err| {
242 self.index += amt_printed;119 context.result = err;
243 }120 return false;
244121 };
245 pub fn printAsciiChar(self: &OutStream, c: u8) -> %void {122 return true;
246 if (self.index + 1 >= self.buffer.len) {
247 %return self.flush();
248 }
249 self.buffer[self.index] = c;
250 self.index += 1;
251 }123 }
252124
253 pub fn flush(self: &OutStream) -> %void {125 pub fn flush(self: &OutStream) -> %void {
...@@ -506,90 +378,6 @@ pub const InStream = struct {...@@ -506,90 +378,6 @@ pub const InStream = struct {
506 }378 }
507};379};
508380
509pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
510 var x: T = 0;
511
512 for (buf) |c| {
513 const digit = %return charToDigit(c, radix);
514 x = %return math.mulOverflow(T, x, radix);
515 x = %return math.addOverflow(T, x, digit);
516 }
517
518 return x;
519}
520
521error InvalidChar;
522fn charToDigit(c: u8, radix: u8) -> %u8 {
523 const value = switch (c) {
524 '0' ... '9' => c - '0',
525 'A' ... 'Z' => c - 'A' + 10,
526 'a' ... 'z' => c - 'a' + 10,
527 else => return error.InvalidChar,
528 };
529
530 if (value >= radix)
531 return error.InvalidChar;
532
533 return value;
534}
535
536fn digitToChar(digit: u8, uppercase: bool) -> u8 {
537 return switch (digit) {
538 0 ... 9 => digit + '0',
539 10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10),
540 else => @unreachable(),
541 };
542}
543
544/// Guaranteed to not use more than max_int_digits
545pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
546 if (@typeOf(x).is_signed)
547 bufPrintSigned(out_buf, x, base, uppercase, width)
548 else
549 bufPrintUnsigned(out_buf, x, base, uppercase, width)
550}
551
552fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
553 const uint = @intType(false, @typeOf(x).bit_count);
554 if (x < 0) {
555 out_buf[0] = '-';
556 const new_value = uint(-(x + 1)) + 1;
557 const new_width = if (width == 0) 0 else (width - 1);
558 return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width);
559 } else if (width == 0) {
560 return bufPrintUnsigned(out_buf, uint(x), base, uppercase, width);
561 } else {
562 out_buf[0] = '+';
563 const new_value = uint(x);
564 const new_width = if (width == 0) 0 else (width - 1);
565 return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width);
566 }
567}
568
569fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
570 // max_int_digits accounts for the minus sign. when printing an unsigned
571 // number we don't need to do that.
572 var buf: [max_int_digits - 1]u8 = undefined;
573 var a = x;
574 var index: usize = buf.len;
575
576 while (true) {
577 const digit = a % base;
578 index -= 1;
579 buf[index] = digitToChar(u8(digit), uppercase);
580 a /= base;
581 if (a == 0)
582 break;
583 }
584
585 const src_buf = buf[index...];
586 const padding = if (width > src_buf.len) (width - src_buf.len) else 0;
587
588 mem.set(u8, out_buf[0...padding], '0');
589 mem.copy(u8, out_buf[padding...], src_buf);
590 return src_buf.len + padding;
591}
592
593pub fn openSelfExe(stream: &InStream) -> %void {381pub fn openSelfExe(stream: &InStream) -> %void {
594 switch (@compileVar("os")) {382 switch (@compileVar("os")) {
595 Os.linux => {383 Os.linux => {
...@@ -602,45 +390,3 @@ pub fn openSelfExe(stream: &InStream) -> %void {...@@ -602,45 +390,3 @@ pub fn openSelfExe(stream: &InStream) -> %void {
602 else => @compileError("unsupported os"),390 else => @compileError("unsupported os"),
603 }391 }
604}392}
605
606fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 {
607 return buf[0...bufPrintInt(buf, value, base, uppercase, width)];
608}
609
610fn testParseU64DigitTooBig() {
611 @setFnTest(this);
612
613 parseUnsigned(u64, "123a", 10) %% |err| {
614 if (err == error.InvalidChar) return;
615 @unreachable();
616 };
617 @unreachable();
618}
619
620fn testParseUnsignedComptime() {
621 @setFnTest(this);
622
623 comptime {
624 assert(%%parseUnsigned(usize, "2", 10) == 2);
625 }
626}
627
628fn testBufPrintInt() {
629 @setFnTest(this);
630
631 var buffer: [max_int_digits]u8 = undefined;
632 const buf = buffer[0...];
633 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
634 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));
635 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));
636 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));
637
638 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));
639
640 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));
641 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));
642 assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));
643
644 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));
645 assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));
646}
test/cases/enum_with_members.zig+3-3
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const io = @import("std").io;3const fmt = @import("std").fmt;
44
5const ET = enum {5const ET = enum {
6 SINT: i32,6 SINT: i32,
...@@ -8,8 +8,8 @@ const ET = enum {...@@ -8,8 +8,8 @@ const ET = enum {
88
9 pub fn print(a: &const ET, buf: []u8) -> %usize {9 pub fn print(a: &const ET, buf: []u8) -> %usize {
10 return switch (*a) {10 return switch (*a) {
11 ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) },11 ET.SINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) },
12 ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) },12 ET.UINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) },
13 }13 }
14 }14 }
15};15};