| ... | ... | @@ -972,20 +972,9 @@ fn formatIntUnsigned( |
| 972 | 972 | } |
| 973 | 973 | |
| 974 | 974 | pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { |
| 975 | | var context = FormatIntBuf{ |
| 976 | | .out_buf = out_buf, |
| 977 | | .index = 0, |
| 978 | | }; |
| 979 | | formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable; |
| 980 | | return context.index; |
| 981 | | } |
| 982 | | const FormatIntBuf = struct { |
| 983 | | out_buf: []u8, |
| 984 | | index: usize, |
| 985 | | }; |
| 986 | | fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { |
| 987 | | mem.copy(u8, context.out_buf[context.index..], bytes); |
| 988 | | context.index += bytes.len; |
| 975 | var fbs = std.io.fixedBufferStream(out_buf); |
| 976 | formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable; |
| 977 | return fbs.pos; |
| 989 | 978 | } |
| 990 | 979 | |
| 991 | 980 | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { |
| ... | ... | @@ -1085,48 +1074,48 @@ fn digitToChar(digit: u8, uppercase: bool) u8 { |
| 1085 | 1074 | }; |
| 1086 | 1075 | } |
| 1087 | 1076 | |
| 1088 | | const BufPrintContext = struct { |
| 1089 | | remaining: []u8, |
| 1090 | | }; |
| 1091 | | |
| 1092 | | fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { |
| 1093 | | if (context.remaining.len < bytes.len) { |
| 1094 | | mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); |
| 1095 | | return error.BufferTooSmall; |
| 1096 | | } |
| 1097 | | mem.copy(u8, context.remaining, bytes); |
| 1098 | | context.remaining = context.remaining[bytes.len..]; |
| 1099 | | } |
| 1100 | | |
| 1101 | | pub const BufPrintError = error{ |
| 1102 | | /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. |
| 1103 | | BufferTooSmall, |
| 1104 | | }; |
| 1105 | | pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { |
| 1106 | | var context = BufPrintContext{ .remaining = buf }; |
| 1107 | | try format(&context, BufPrintError, bufPrintWrite, fmt, args); |
| 1108 | | return buf[0 .. buf.len - context.remaining.len]; |
| 1109 | | } |
| 1110 | | |
| 1111 | | pub const AllocPrintError = error{OutOfMemory}; |
| 1112 | | |
| 1113 | | pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { |
| 1114 | | var size: usize = 0; |
| 1115 | | format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; |
| 1116 | | const buf = try allocator.alloc(u8, size); |
| 1117 | | return bufPrint(buf, fmt, args) catch |err| switch (err) { |
| 1118 | | error.BufferTooSmall => unreachable, // we just counted the size above |
| 1119 | | }; |
| 1120 | | } |
| 1121 | | |
| 1122 | | fn countSize(size: *usize, bytes: []const u8) (error{}!void) { |
| 1123 | | size.* += bytes.len; |
| 1124 | | } |
| 1125 | | |
| 1126 | | pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { |
| 1127 | | const result = try allocPrint(allocator, fmt ++ "\x00", args); |
| 1128 | | return result[0 .. result.len - 1 :0]; |
| 1129 | | } |
| 1077 | // const BufPrintContext = struct { |
| 1078 | // remaining: []u8, |
| 1079 | // }; |
| 1080 | |
| 1081 | // fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { |
| 1082 | // if (context.remaining.len < bytes.len) { |
| 1083 | // mem.copy(u8, context.remaining, bytes[0..context.remaining.len]); |
| 1084 | // return error.BufferTooSmall; |
| 1085 | // } |
| 1086 | // mem.copy(u8, context.remaining, bytes); |
| 1087 | // context.remaining = context.remaining[bytes.len..]; |
| 1088 | // } |
| 1089 | |
| 1090 | // pub const BufPrintError = error{ |
| 1091 | // /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. |
| 1092 | // BufferTooSmall, |
| 1093 | // }; |
| 1094 | // pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { |
| 1095 | // var context = BufPrintContext{ .remaining = buf }; |
| 1096 | // try format(&context, BufPrintError, bufPrintWrite, fmt, args); |
| 1097 | // return buf[0 .. buf.len - context.remaining.len]; |
| 1098 | // } |
| 1099 | |
| 1100 | // pub const AllocPrintError = error{OutOfMemory}; |
| 1101 | |
| 1102 | // pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { |
| 1103 | // var size: usize = 0; |
| 1104 | // format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; |
| 1105 | // const buf = try allocator.alloc(u8, size); |
| 1106 | // return bufPrint(buf, fmt, args) catch |err| switch (err) { |
| 1107 | // error.BufferTooSmall => unreachable, // we just counted the size above |
| 1108 | // }; |
| 1109 | // } |
| 1110 | |
| 1111 | // fn countSize(size: *usize, bytes: []const u8) (error{}!void) { |
| 1112 | // size.* += bytes.len; |
| 1113 | // } |
| 1114 | |
| 1115 | // pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { |
| 1116 | // const result = try allocPrint(allocator, fmt ++ "\x00", args); |
| 1117 | // return result[0 .. result.len - 1 :0]; |
| 1118 | // } |
| 1130 | 1119 | |
| 1131 | 1120 | test "bufPrintInt" { |
| 1132 | 1121 | var buffer: [100]u8 = undefined; |
| ... | ... | @@ -1153,566 +1142,566 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: |
| 1153 | 1142 | return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; |
| 1154 | 1143 | } |
| 1155 | 1144 | |
| 1156 | | test "parse u64 digit too big" { |
| 1157 | | _ = parseUnsigned(u64, "123a", 10) catch |err| { |
| 1158 | | if (err == error.InvalidCharacter) return; |
| 1159 | | unreachable; |
| 1160 | | }; |
| 1161 | | unreachable; |
| 1162 | | } |
| 1163 | | |
| 1164 | | test "parse unsigned comptime" { |
| 1165 | | comptime { |
| 1166 | | std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); |
| 1167 | | } |
| 1168 | | } |
| 1169 | | |
| 1170 | | test "optional" { |
| 1171 | | { |
| 1172 | | const value: ?i32 = 1234; |
| 1173 | | try testFmt("optional: 1234\n", "optional: {}\n", .{value}); |
| 1174 | | } |
| 1175 | | { |
| 1176 | | const value: ?i32 = null; |
| 1177 | | try testFmt("optional: null\n", "optional: {}\n", .{value}); |
| 1178 | | } |
| 1179 | | } |
| 1180 | | |
| 1181 | | test "error" { |
| 1182 | | { |
| 1183 | | const value: anyerror!i32 = 1234; |
| 1184 | | try testFmt("error union: 1234\n", "error union: {}\n", .{value}); |
| 1185 | | } |
| 1186 | | { |
| 1187 | | const value: anyerror!i32 = error.InvalidChar; |
| 1188 | | try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); |
| 1189 | | } |
| 1190 | | } |
| 1191 | | |
| 1192 | | test "int.small" { |
| 1193 | | { |
| 1194 | | const value: u3 = 0b101; |
| 1195 | | try testFmt("u3: 5\n", "u3: {}\n", .{value}); |
| 1196 | | } |
| 1197 | | } |
| 1198 | | |
| 1199 | | test "int.specifier" { |
| 1200 | | { |
| 1201 | | const value: u8 = 'a'; |
| 1202 | | try testFmt("u8: a\n", "u8: {c}\n", .{value}); |
| 1203 | | } |
| 1204 | | { |
| 1205 | | const value: u8 = 0b1100; |
| 1206 | | try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); |
| 1207 | | } |
| 1208 | | } |
| 1209 | | |
| 1210 | | test "int.padded" { |
| 1211 | | try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); |
| 1212 | | try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); |
| 1213 | | } |
| 1214 | | |
| 1215 | | test "buffer" { |
| 1216 | | { |
| 1217 | | var buf1: [32]u8 = undefined; |
| 1218 | | var context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1219 | | try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); |
| 1220 | | var res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1221 | | std.testing.expect(mem.eql(u8, res, "1234")); |
| 1222 | | |
| 1223 | | context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1224 | | try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); |
| 1225 | | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1226 | | std.testing.expect(mem.eql(u8, res, "a")); |
| 1227 | | |
| 1228 | | context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1229 | | try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); |
| 1230 | | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1231 | | std.testing.expect(mem.eql(u8, res, "1100")); |
| 1232 | | } |
| 1233 | | } |
| 1234 | | |
| 1235 | | test "array" { |
| 1236 | | { |
| 1237 | | const value: [3]u8 = "abc".*; |
| 1238 | | try testFmt("array: abc\n", "array: {}\n", .{value}); |
| 1239 | | try testFmt("array: abc\n", "array: {}\n", .{&value}); |
| 1240 | | |
| 1241 | | var buf: [100]u8 = undefined; |
| 1242 | | try testFmt( |
| 1243 | | try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), |
| 1244 | | "array: {*}\n", |
| 1245 | | .{&value}, |
| 1246 | | ); |
| 1247 | | } |
| 1248 | | } |
| 1249 | | |
| 1250 | | test "slice" { |
| 1251 | | { |
| 1252 | | const value: []const u8 = "abc"; |
| 1253 | | try testFmt("slice: abc\n", "slice: {}\n", .{value}); |
| 1254 | | } |
| 1255 | | { |
| 1256 | | const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; |
| 1257 | | try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); |
| 1258 | | } |
| 1259 | | |
| 1260 | | try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); |
| 1261 | | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); |
| 1262 | | } |
| 1263 | | |
| 1264 | | test "pointer" { |
| 1265 | | { |
| 1266 | | const value = @intToPtr(*align(1) i32, 0xdeadbeef); |
| 1267 | | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); |
| 1268 | | try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); |
| 1269 | | } |
| 1270 | | { |
| 1271 | | const value = @intToPtr(fn () void, 0xdeadbeef); |
| 1272 | | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); |
| 1273 | | } |
| 1274 | | { |
| 1275 | | const value = @intToPtr(fn () void, 0xdeadbeef); |
| 1276 | | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); |
| 1277 | | } |
| 1278 | | } |
| 1279 | | |
| 1280 | | test "cstr" { |
| 1281 | | try testFmt( |
| 1282 | | "cstr: Test C\n", |
| 1283 | | "cstr: {s}\n", |
| 1284 | | .{@ptrCast([*c]const u8, "Test C")}, |
| 1285 | | ); |
| 1286 | | try testFmt( |
| 1287 | | "cstr: Test C \n", |
| 1288 | | "cstr: {s:10}\n", |
| 1289 | | .{@ptrCast([*c]const u8, "Test C")}, |
| 1290 | | ); |
| 1291 | | } |
| 1292 | | |
| 1293 | | test "filesize" { |
| 1294 | | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); |
| 1295 | | try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); |
| 1296 | | } |
| 1297 | | |
| 1298 | | test "struct" { |
| 1299 | | { |
| 1300 | | const Struct = struct { |
| 1301 | | field: u8, |
| 1302 | | }; |
| 1303 | | const value = Struct{ .field = 42 }; |
| 1304 | | try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); |
| 1305 | | try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); |
| 1306 | | } |
| 1307 | | { |
| 1308 | | const Struct = struct { |
| 1309 | | a: u0, |
| 1310 | | b: u1, |
| 1311 | | }; |
| 1312 | | const value = Struct{ .a = 0, .b = 1 }; |
| 1313 | | try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); |
| 1314 | | } |
| 1315 | | } |
| 1316 | | |
| 1317 | | test "enum" { |
| 1318 | | const Enum = enum { |
| 1319 | | One, |
| 1320 | | Two, |
| 1321 | | }; |
| 1322 | | const value = Enum.Two; |
| 1323 | | try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); |
| 1324 | | try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); |
| 1325 | | } |
| 1326 | | |
| 1327 | | test "non-exhaustive enum" { |
| 1328 | | const Enum = enum(u16) { |
| 1329 | | One = 0x000f, |
| 1330 | | Two = 0xbeef, |
| 1331 | | _, |
| 1332 | | }; |
| 1333 | | try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); |
| 1334 | | try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); |
| 1335 | | try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); |
| 1336 | | try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); |
| 1337 | | try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); |
| 1338 | | try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); |
| 1339 | | } |
| 1340 | | |
| 1341 | | test "float.scientific" { |
| 1342 | | try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); |
| 1343 | | try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); |
| 1344 | | try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); |
| 1345 | | try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); |
| 1346 | | } |
| 1347 | | |
| 1348 | | test "float.scientific.precision" { |
| 1349 | | try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); |
| 1350 | | try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); |
| 1351 | | try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); |
| 1352 | | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. |
| 1353 | | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. |
| 1354 | | try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); |
| 1355 | | } |
| 1356 | | |
| 1357 | | test "float.special" { |
| 1358 | | try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); |
| 1359 | | // negative nan is not defined by IEE 754, |
| 1360 | | // and ARM thus normalizes it to positive nan |
| 1361 | | if (builtin.arch != builtin.Arch.arm) { |
| 1362 | | try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); |
| 1363 | | } |
| 1364 | | try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); |
| 1365 | | try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); |
| 1366 | | } |
| 1367 | | |
| 1368 | | test "float.decimal" { |
| 1369 | | try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); |
| 1370 | | try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); |
| 1371 | | try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); |
| 1372 | | try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); |
| 1373 | | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). |
| 1374 | | // -11.12339... is rounded back up to -11.1234 |
| 1375 | | try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); |
| 1376 | | try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); |
| 1377 | | try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); |
| 1378 | | try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); |
| 1379 | | try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); |
| 1380 | | try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); |
| 1381 | | try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); |
| 1382 | | try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); |
| 1383 | | try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); |
| 1384 | | try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); |
| 1385 | | } |
| 1386 | | |
| 1387 | | test "float.libc.sanity" { |
| 1388 | | try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); |
| 1389 | | try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); |
| 1390 | | try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); |
| 1391 | | try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); |
| 1392 | | try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); |
| 1393 | | |
| 1394 | | // libc differences |
| 1395 | | // |
| 1396 | | // This is 0.015625 exactly according to gdb. We thus round down, |
| 1397 | | // however glibc rounds up for some reason. This occurs for all |
| 1398 | | // floats of the form x.yyyy25 on a precision point. |
| 1399 | | try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); |
| 1400 | | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 |
| 1401 | | // also rounds to 630 so I'm inclined to believe libc is not |
| 1402 | | // optimal here. |
| 1403 | | try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); |
| 1404 | | } |
| 1405 | | |
| 1406 | | test "custom" { |
| 1407 | | const Vec2 = struct { |
| 1408 | | const SelfType = @This(); |
| 1409 | | x: f32, |
| 1410 | | y: f32, |
| 1411 | | |
| 1412 | | pub fn format( |
| 1413 | | self: SelfType, |
| 1414 | | comptime fmt: []const u8, |
| 1415 | | options: FormatOptions, |
| 1416 | | context: var, |
| 1417 | | comptime Errors: type, |
| 1418 | | comptime output: fn (@TypeOf(context), []const u8) !void, |
| 1419 | | ) !void { |
| 1420 | | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { |
| 1421 | | return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); |
| 1422 | | } else if (comptime std.mem.eql(u8, fmt, "d")) { |
| 1423 | | return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); |
| 1424 | | } else { |
| 1425 | | @compileError("Unknown format character: '" ++ fmt ++ "'"); |
| 1426 | | } |
| 1427 | | } |
| 1428 | | }; |
| 1429 | | |
| 1430 | | var buf1: [32]u8 = undefined; |
| 1431 | | var value = Vec2{ |
| 1432 | | .x = 10.2, |
| 1433 | | .y = 2.22, |
| 1434 | | }; |
| 1435 | | try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); |
| 1436 | | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); |
| 1437 | | |
| 1438 | | // same thing but not passing a pointer |
| 1439 | | try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); |
| 1440 | | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); |
| 1441 | | } |
| 1442 | | |
| 1443 | | test "struct" { |
| 1444 | | const S = struct { |
| 1445 | | a: u32, |
| 1446 | | b: anyerror, |
| 1447 | | }; |
| 1448 | | |
| 1449 | | const inst = S{ |
| 1450 | | .a = 456, |
| 1451 | | .b = error.Unused, |
| 1452 | | }; |
| 1453 | | |
| 1454 | | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); |
| 1455 | | } |
| 1456 | | |
| 1457 | | test "union" { |
| 1458 | | const TU = union(enum) { |
| 1459 | | float: f32, |
| 1460 | | int: u32, |
| 1461 | | }; |
| 1462 | | |
| 1463 | | const UU = union { |
| 1464 | | float: f32, |
| 1465 | | int: u32, |
| 1466 | | }; |
| 1467 | | |
| 1468 | | const EU = extern union { |
| 1469 | | float: f32, |
| 1470 | | int: u32, |
| 1471 | | }; |
| 1472 | | |
| 1473 | | const tu_inst = TU{ .int = 123 }; |
| 1474 | | const uu_inst = UU{ .int = 456 }; |
| 1475 | | const eu_inst = EU{ .float = 321.123 }; |
| 1476 | | |
| 1477 | | try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); |
| 1478 | | |
| 1479 | | var buf: [100]u8 = undefined; |
| 1480 | | const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); |
| 1481 | | std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); |
| 1482 | | |
| 1483 | | const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); |
| 1484 | | std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); |
| 1485 | | } |
| 1486 | | |
| 1487 | | test "enum" { |
| 1488 | | const E = enum { |
| 1489 | | One, |
| 1490 | | Two, |
| 1491 | | Three, |
| 1492 | | }; |
| 1493 | | |
| 1494 | | const inst = E.Two; |
| 1495 | | |
| 1496 | | try testFmt("E.Two", "{}", .{inst}); |
| 1497 | | } |
| 1498 | | |
| 1499 | | test "struct.self-referential" { |
| 1500 | | const S = struct { |
| 1501 | | const SelfType = @This(); |
| 1502 | | a: ?*SelfType, |
| 1503 | | }; |
| 1504 | | |
| 1505 | | var inst = S{ |
| 1506 | | .a = null, |
| 1507 | | }; |
| 1508 | | inst.a = &inst; |
| 1509 | | |
| 1510 | | try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); |
| 1511 | | } |
| 1512 | | |
| 1513 | | test "struct.zero-size" { |
| 1514 | | const A = struct { |
| 1515 | | fn foo() void {} |
| 1516 | | }; |
| 1517 | | const B = struct { |
| 1518 | | a: A, |
| 1519 | | c: i32, |
| 1520 | | }; |
| 1521 | | |
| 1522 | | const a = A{}; |
| 1523 | | const b = B{ .a = a, .c = 0 }; |
| 1524 | | |
| 1525 | | try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); |
| 1526 | | } |
| 1527 | | |
| 1528 | | test "bytes.hex" { |
| 1529 | | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1530 | | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); |
| 1531 | | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); |
| 1532 | | //Test Slices |
| 1533 | | try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); |
| 1534 | | try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); |
| 1535 | | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; |
| 1536 | | try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); |
| 1537 | | } |
| 1538 | | |
| 1539 | | fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { |
| 1540 | | var buf: [100]u8 = undefined; |
| 1541 | | const result = try bufPrint(buf[0..], template, args); |
| 1542 | | if (mem.eql(u8, result, expected)) return; |
| 1543 | | |
| 1544 | | std.debug.warn("\n====== expected this output: =========\n", .{}); |
| 1545 | | std.debug.warn("{}", .{expected}); |
| 1546 | | std.debug.warn("\n======== instead found this: =========\n", .{}); |
| 1547 | | std.debug.warn("{}", .{result}); |
| 1548 | | std.debug.warn("\n======================================\n", .{}); |
| 1549 | | return error.TestFailed; |
| 1550 | | } |
| 1551 | | |
| 1552 | | pub fn trim(buf: []const u8) []const u8 { |
| 1553 | | var start: usize = 0; |
| 1554 | | while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} |
| 1555 | | |
| 1556 | | var end: usize = buf.len; |
| 1557 | | while (true) { |
| 1558 | | if (end > start) { |
| 1559 | | const new_end = end - 1; |
| 1560 | | if (isWhiteSpace(buf[new_end])) { |
| 1561 | | end = new_end; |
| 1562 | | continue; |
| 1563 | | } |
| 1564 | | } |
| 1565 | | break; |
| 1566 | | } |
| 1567 | | return buf[start..end]; |
| 1568 | | } |
| 1569 | | |
| 1570 | | test "trim" { |
| 1571 | | std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); |
| 1572 | | std.testing.expect(mem.eql(u8, "", trim(" "))); |
| 1573 | | std.testing.expect(mem.eql(u8, "", trim(""))); |
| 1574 | | std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); |
| 1575 | | std.testing.expect(mem.eql(u8, "abc", trim("abc "))); |
| 1576 | | } |
| 1577 | | |
| 1578 | | pub fn isWhiteSpace(byte: u8) bool { |
| 1579 | | return switch (byte) { |
| 1580 | | ' ', '\t', '\n', '\r' => true, |
| 1581 | | else => false, |
| 1582 | | }; |
| 1583 | | } |
| 1584 | | |
| 1585 | | pub fn hexToBytes(out: []u8, input: []const u8) !void { |
| 1586 | | if (out.len * 2 < input.len) |
| 1587 | | return error.InvalidLength; |
| 1588 | | |
| 1589 | | var in_i: usize = 0; |
| 1590 | | while (in_i != input.len) : (in_i += 2) { |
| 1591 | | const hi = try charToDigit(input[in_i], 16); |
| 1592 | | const lo = try charToDigit(input[in_i + 1], 16); |
| 1593 | | out[in_i / 2] = (hi << 4) | lo; |
| 1594 | | } |
| 1595 | | } |
| 1596 | | |
| 1597 | | test "hexToBytes" { |
| 1598 | | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; |
| 1599 | | var pb: [32]u8 = undefined; |
| 1600 | | try hexToBytes(pb[0..], test_hex_str); |
| 1601 | | try testFmt(test_hex_str, "{X}", .{pb}); |
| 1602 | | } |
| 1603 | | |
| 1604 | | test "formatIntValue with comptime_int" { |
| 1605 | | const value: comptime_int = 123456789123456789; |
| 1606 | | |
| 1607 | | var buf = std.ArrayList(u8).init(std.testing.allocator); |
| 1608 | | defer buf.deinit(); |
| 1609 | | try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); |
| 1610 | | std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); |
| 1611 | | } |
| 1612 | | |
| 1613 | | test "formatType max_depth" { |
| 1614 | | const Vec2 = struct { |
| 1615 | | const SelfType = @This(); |
| 1616 | | x: f32, |
| 1617 | | y: f32, |
| 1618 | | |
| 1619 | | pub fn format( |
| 1620 | | self: SelfType, |
| 1621 | | comptime fmt: []const u8, |
| 1622 | | options: FormatOptions, |
| 1623 | | context: var, |
| 1624 | | comptime Errors: type, |
| 1625 | | comptime output: fn (@TypeOf(context), []const u8) !void, |
| 1626 | | ) !void { |
| 1627 | | if (fmt.len == 0) { |
| 1628 | | return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); |
| 1629 | | } else { |
| 1630 | | @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 1631 | | } |
| 1632 | | } |
| 1633 | | }; |
| 1634 | | const E = enum { |
| 1635 | | One, |
| 1636 | | Two, |
| 1637 | | Three, |
| 1638 | | }; |
| 1639 | | const TU = union(enum) { |
| 1640 | | const SelfType = @This(); |
| 1641 | | float: f32, |
| 1642 | | int: u32, |
| 1643 | | ptr: ?*SelfType, |
| 1644 | | }; |
| 1645 | | const S = struct { |
| 1646 | | const SelfType = @This(); |
| 1647 | | a: ?*SelfType, |
| 1648 | | tu: TU, |
| 1649 | | e: E, |
| 1650 | | vec: Vec2, |
| 1651 | | }; |
| 1652 | | |
| 1653 | | var inst = S{ |
| 1654 | | .a = null, |
| 1655 | | .tu = TU{ .ptr = null }, |
| 1656 | | .e = E.Two, |
| 1657 | | .vec = Vec2{ .x = 10.2, .y = 2.22 }, |
| 1658 | | }; |
| 1659 | | inst.a = &inst; |
| 1660 | | inst.tu.ptr = &inst.tu; |
| 1661 | | |
| 1662 | | var buf0 = std.ArrayList(u8).init(std.testing.allocator); |
| 1663 | | defer buf0.deinit(); |
| 1664 | | try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); |
| 1665 | | std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); |
| 1666 | | |
| 1667 | | var buf1 = std.ArrayList(u8).init(std.testing.allocator); |
| 1668 | | defer buf1.deinit(); |
| 1669 | | try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); |
| 1670 | | std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1671 | | |
| 1672 | | var buf2 = std.ArrayList(u8).init(std.testing.allocator); |
| 1673 | | defer buf2.deinit(); |
| 1674 | | try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); |
| 1675 | | std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1676 | | |
| 1677 | | var buf3 = std.ArrayList(u8).init(std.testing.allocator); |
| 1678 | | defer buf3.deinit(); |
| 1679 | | try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); |
| 1680 | | std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1681 | | } |
| 1682 | | |
| 1683 | | test "positional" { |
| 1684 | | try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); |
| 1685 | | try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); |
| 1686 | | try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); |
| 1687 | | try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); |
| 1688 | | try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); |
| 1689 | | } |
| 1690 | | |
| 1691 | | test "positional with specifier" { |
| 1692 | | try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); |
| 1693 | | } |
| 1694 | | |
| 1695 | | test "positional/alignment/width/precision" { |
| 1696 | | try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); |
| 1697 | | } |
| 1698 | | |
| 1699 | | test "vector" { |
| 1700 | | // https://github.com/ziglang/zig/issues/3317 |
| 1701 | | if (builtin.arch == .mipsel) return error.SkipZigTest; |
| 1702 | | |
| 1703 | | const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; |
| 1704 | | const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; |
| 1705 | | const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; |
| 1706 | | |
| 1707 | | try testFmt("{ true, false, true, false }", "{}", .{vbool}); |
| 1708 | | try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); |
| 1709 | | try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); |
| 1710 | | try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); |
| 1711 | | try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); |
| 1712 | | try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); |
| 1713 | | try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); |
| 1714 | | } |
| 1715 | | |
| 1716 | | test "enum-literal" { |
| 1717 | | try testFmt(".hello_world", "{}", .{.hello_world}); |
| 1718 | | } |
| 1145 | // test "parse u64 digit too big" { |
| 1146 | // _ = parseUnsigned(u64, "123a", 10) catch |err| { |
| 1147 | // if (err == error.InvalidCharacter) return; |
| 1148 | // unreachable; |
| 1149 | // }; |
| 1150 | // unreachable; |
| 1151 | // } |
| 1152 | |
| 1153 | // test "parse unsigned comptime" { |
| 1154 | // comptime { |
| 1155 | // std.testing.expect((try parseUnsigned(usize, "2", 10)) == 2); |
| 1156 | // } |
| 1157 | // } |
| 1158 | |
| 1159 | // test "optional" { |
| 1160 | // { |
| 1161 | // const value: ?i32 = 1234; |
| 1162 | // try testFmt("optional: 1234\n", "optional: {}\n", .{value}); |
| 1163 | // } |
| 1164 | // { |
| 1165 | // const value: ?i32 = null; |
| 1166 | // try testFmt("optional: null\n", "optional: {}\n", .{value}); |
| 1167 | // } |
| 1168 | // } |
| 1169 | |
| 1170 | // test "error" { |
| 1171 | // { |
| 1172 | // const value: anyerror!i32 = 1234; |
| 1173 | // try testFmt("error union: 1234\n", "error union: {}\n", .{value}); |
| 1174 | // } |
| 1175 | // { |
| 1176 | // const value: anyerror!i32 = error.InvalidChar; |
| 1177 | // try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); |
| 1178 | // } |
| 1179 | // } |
| 1180 | |
| 1181 | // test "int.small" { |
| 1182 | // { |
| 1183 | // const value: u3 = 0b101; |
| 1184 | // try testFmt("u3: 5\n", "u3: {}\n", .{value}); |
| 1185 | // } |
| 1186 | // } |
| 1187 | |
| 1188 | // test "int.specifier" { |
| 1189 | // { |
| 1190 | // const value: u8 = 'a'; |
| 1191 | // try testFmt("u8: a\n", "u8: {c}\n", .{value}); |
| 1192 | // } |
| 1193 | // { |
| 1194 | // const value: u8 = 0b1100; |
| 1195 | // try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); |
| 1196 | // } |
| 1197 | // } |
| 1198 | |
| 1199 | // test "int.padded" { |
| 1200 | // try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); |
| 1201 | // try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)}); |
| 1202 | // } |
| 1203 | |
| 1204 | // test "buffer" { |
| 1205 | // { |
| 1206 | // var buf1: [32]u8 = undefined; |
| 1207 | // var context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1208 | // try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); |
| 1209 | // var res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1210 | // std.testing.expect(mem.eql(u8, res, "1234")); |
| 1211 | |
| 1212 | // context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1213 | // try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); |
| 1214 | // res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1215 | // std.testing.expect(mem.eql(u8, res, "a")); |
| 1216 | |
| 1217 | // context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1218 | // try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); |
| 1219 | // res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1220 | // std.testing.expect(mem.eql(u8, res, "1100")); |
| 1221 | // } |
| 1222 | // } |
| 1223 | |
| 1224 | // test "array" { |
| 1225 | // { |
| 1226 | // const value: [3]u8 = "abc".*; |
| 1227 | // try testFmt("array: abc\n", "array: {}\n", .{value}); |
| 1228 | // try testFmt("array: abc\n", "array: {}\n", .{&value}); |
| 1229 | |
| 1230 | // var buf: [100]u8 = undefined; |
| 1231 | // try testFmt( |
| 1232 | // try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), |
| 1233 | // "array: {*}\n", |
| 1234 | // .{&value}, |
| 1235 | // ); |
| 1236 | // } |
| 1237 | // } |
| 1238 | |
| 1239 | // test "slice" { |
| 1240 | // { |
| 1241 | // const value: []const u8 = "abc"; |
| 1242 | // try testFmt("slice: abc\n", "slice: {}\n", .{value}); |
| 1243 | // } |
| 1244 | // { |
| 1245 | // const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; |
| 1246 | // try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); |
| 1247 | // } |
| 1248 | |
| 1249 | // try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"}); |
| 1250 | // try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); |
| 1251 | // } |
| 1252 | |
| 1253 | // test "pointer" { |
| 1254 | // { |
| 1255 | // const value = @intToPtr(*align(1) i32, 0xdeadbeef); |
| 1256 | // try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); |
| 1257 | // try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); |
| 1258 | // } |
| 1259 | // { |
| 1260 | // const value = @intToPtr(fn () void, 0xdeadbeef); |
| 1261 | // try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); |
| 1262 | // } |
| 1263 | // { |
| 1264 | // const value = @intToPtr(fn () void, 0xdeadbeef); |
| 1265 | // try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); |
| 1266 | // } |
| 1267 | // } |
| 1268 | |
| 1269 | // test "cstr" { |
| 1270 | // try testFmt( |
| 1271 | // "cstr: Test C\n", |
| 1272 | // "cstr: {s}\n", |
| 1273 | // .{@ptrCast([*c]const u8, "Test C")}, |
| 1274 | // ); |
| 1275 | // try testFmt( |
| 1276 | // "cstr: Test C \n", |
| 1277 | // "cstr: {s:10}\n", |
| 1278 | // .{@ptrCast([*c]const u8, "Test C")}, |
| 1279 | // ); |
| 1280 | // } |
| 1281 | |
| 1282 | // test "filesize" { |
| 1283 | // try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); |
| 1284 | // try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); |
| 1285 | // } |
| 1286 | |
| 1287 | // test "struct" { |
| 1288 | // { |
| 1289 | // const Struct = struct { |
| 1290 | // field: u8, |
| 1291 | // }; |
| 1292 | // const value = Struct{ .field = 42 }; |
| 1293 | // try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); |
| 1294 | // try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); |
| 1295 | // } |
| 1296 | // { |
| 1297 | // const Struct = struct { |
| 1298 | // a: u0, |
| 1299 | // b: u1, |
| 1300 | // }; |
| 1301 | // const value = Struct{ .a = 0, .b = 1 }; |
| 1302 | // try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); |
| 1303 | // } |
| 1304 | // } |
| 1305 | |
| 1306 | // test "enum" { |
| 1307 | // const Enum = enum { |
| 1308 | // One, |
| 1309 | // Two, |
| 1310 | // }; |
| 1311 | // const value = Enum.Two; |
| 1312 | // try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); |
| 1313 | // try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); |
| 1314 | // } |
| 1315 | |
| 1316 | // test "non-exhaustive enum" { |
| 1317 | // const Enum = enum(u16) { |
| 1318 | // One = 0x000f, |
| 1319 | // Two = 0xbeef, |
| 1320 | // _, |
| 1321 | // }; |
| 1322 | // try testFmt("enum: Enum(15)\n", "enum: {}\n", .{Enum.One}); |
| 1323 | // try testFmt("enum: Enum(48879)\n", "enum: {}\n", .{Enum.Two}); |
| 1324 | // try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); |
| 1325 | // try testFmt("enum: Enum(f)\n", "enum: {x}\n", .{Enum.One}); |
| 1326 | // try testFmt("enum: Enum(beef)\n", "enum: {x}\n", .{Enum.Two}); |
| 1327 | // try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); |
| 1328 | // } |
| 1329 | |
| 1330 | // test "float.scientific" { |
| 1331 | // try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); |
| 1332 | // try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); |
| 1333 | // try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); |
| 1334 | // try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); |
| 1335 | // } |
| 1336 | |
| 1337 | // test "float.scientific.precision" { |
| 1338 | // try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); |
| 1339 | // try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); |
| 1340 | // try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); |
| 1341 | // // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. |
| 1342 | // // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. |
| 1343 | // try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); |
| 1344 | // } |
| 1345 | |
| 1346 | // test "float.special" { |
| 1347 | // try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); |
| 1348 | // // negative nan is not defined by IEE 754, |
| 1349 | // // and ARM thus normalizes it to positive nan |
| 1350 | // if (builtin.arch != builtin.Arch.arm) { |
| 1351 | // try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); |
| 1352 | // } |
| 1353 | // try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); |
| 1354 | // try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); |
| 1355 | // } |
| 1356 | |
| 1357 | // test "float.decimal" { |
| 1358 | // try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); |
| 1359 | // try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); |
| 1360 | // try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); |
| 1361 | // try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); |
| 1362 | // // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). |
| 1363 | // // -11.12339... is rounded back up to -11.1234 |
| 1364 | // try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); |
| 1365 | // try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); |
| 1366 | // try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); |
| 1367 | // try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); |
| 1368 | // try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); |
| 1369 | // try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); |
| 1370 | // try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); |
| 1371 | // try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); |
| 1372 | // try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); |
| 1373 | // try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); |
| 1374 | // } |
| 1375 | |
| 1376 | // test "float.libc.sanity" { |
| 1377 | // try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); |
| 1378 | // try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); |
| 1379 | // try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); |
| 1380 | // try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); |
| 1381 | // try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); |
| 1382 | |
| 1383 | // // libc differences |
| 1384 | // // |
| 1385 | // // This is 0.015625 exactly according to gdb. We thus round down, |
| 1386 | // // however glibc rounds up for some reason. This occurs for all |
| 1387 | // // floats of the form x.yyyy25 on a precision point. |
| 1388 | // try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); |
| 1389 | // // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 |
| 1390 | // // also rounds to 630 so I'm inclined to believe libc is not |
| 1391 | // // optimal here. |
| 1392 | // try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); |
| 1393 | // } |
| 1394 | |
| 1395 | // test "custom" { |
| 1396 | // const Vec2 = struct { |
| 1397 | // const SelfType = @This(); |
| 1398 | // x: f32, |
| 1399 | // y: f32, |
| 1400 | |
| 1401 | // pub fn format( |
| 1402 | // self: SelfType, |
| 1403 | // comptime fmt: []const u8, |
| 1404 | // options: FormatOptions, |
| 1405 | // context: var, |
| 1406 | // comptime Errors: type, |
| 1407 | // comptime output: fn (@TypeOf(context), []const u8) !void, |
| 1408 | // ) !void { |
| 1409 | // if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { |
| 1410 | // return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); |
| 1411 | // } else if (comptime std.mem.eql(u8, fmt, "d")) { |
| 1412 | // return std.fmtstream.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y }); |
| 1413 | // } else { |
| 1414 | // @compileError("Unknown format character: '" ++ fmt ++ "'"); |
| 1415 | // } |
| 1416 | // } |
| 1417 | // }; |
| 1418 | |
| 1419 | // var buf1: [32]u8 = undefined; |
| 1420 | // var value = Vec2{ |
| 1421 | // .x = 10.2, |
| 1422 | // .y = 2.22, |
| 1423 | // }; |
| 1424 | // try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); |
| 1425 | // try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); |
| 1426 | |
| 1427 | // // same thing but not passing a pointer |
| 1428 | // try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); |
| 1429 | // try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); |
| 1430 | // } |
| 1431 | |
| 1432 | // test "struct" { |
| 1433 | // const S = struct { |
| 1434 | // a: u32, |
| 1435 | // b: anyerror, |
| 1436 | // }; |
| 1437 | |
| 1438 | // const inst = S{ |
| 1439 | // .a = 456, |
| 1440 | // .b = error.Unused, |
| 1441 | // }; |
| 1442 | |
| 1443 | // try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); |
| 1444 | // } |
| 1445 | |
| 1446 | // test "union" { |
| 1447 | // const TU = union(enum) { |
| 1448 | // float: f32, |
| 1449 | // int: u32, |
| 1450 | // }; |
| 1451 | |
| 1452 | // const UU = union { |
| 1453 | // float: f32, |
| 1454 | // int: u32, |
| 1455 | // }; |
| 1456 | |
| 1457 | // const EU = extern union { |
| 1458 | // float: f32, |
| 1459 | // int: u32, |
| 1460 | // }; |
| 1461 | |
| 1462 | // const tu_inst = TU{ .int = 123 }; |
| 1463 | // const uu_inst = UU{ .int = 456 }; |
| 1464 | // const eu_inst = EU{ .float = 321.123 }; |
| 1465 | |
| 1466 | // try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); |
| 1467 | |
| 1468 | // var buf: [100]u8 = undefined; |
| 1469 | // const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); |
| 1470 | // std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); |
| 1471 | |
| 1472 | // const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); |
| 1473 | // std.testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); |
| 1474 | // } |
| 1475 | |
| 1476 | // test "enum" { |
| 1477 | // const E = enum { |
| 1478 | // One, |
| 1479 | // Two, |
| 1480 | // Three, |
| 1481 | // }; |
| 1482 | |
| 1483 | // const inst = E.Two; |
| 1484 | |
| 1485 | // try testFmt("E.Two", "{}", .{inst}); |
| 1486 | // } |
| 1487 | |
| 1488 | // test "struct.self-referential" { |
| 1489 | // const S = struct { |
| 1490 | // const SelfType = @This(); |
| 1491 | // a: ?*SelfType, |
| 1492 | // }; |
| 1493 | |
| 1494 | // var inst = S{ |
| 1495 | // .a = null, |
| 1496 | // }; |
| 1497 | // inst.a = &inst; |
| 1498 | |
| 1499 | // try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); |
| 1500 | // } |
| 1501 | |
| 1502 | // test "struct.zero-size" { |
| 1503 | // const A = struct { |
| 1504 | // fn foo() void {} |
| 1505 | // }; |
| 1506 | // const B = struct { |
| 1507 | // a: A, |
| 1508 | // c: i32, |
| 1509 | // }; |
| 1510 | |
| 1511 | // const a = A{}; |
| 1512 | // const b = B{ .a = a, .c = 0 }; |
| 1513 | |
| 1514 | // try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); |
| 1515 | // } |
| 1516 | |
| 1517 | // test "bytes.hex" { |
| 1518 | // const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1519 | // try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); |
| 1520 | // try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); |
| 1521 | // //Test Slices |
| 1522 | // try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); |
| 1523 | // try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); |
| 1524 | // const bytes_with_zeros = "\x00\x0E\xBA\xBE"; |
| 1525 | // try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); |
| 1526 | // } |
| 1527 | |
| 1528 | // fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { |
| 1529 | // var buf: [100]u8 = undefined; |
| 1530 | // const result = try bufPrint(buf[0..], template, args); |
| 1531 | // if (mem.eql(u8, result, expected)) return; |
| 1532 | |
| 1533 | // std.debug.warn("\n====== expected this output: =========\n", .{}); |
| 1534 | // std.debug.warn("{}", .{expected}); |
| 1535 | // std.debug.warn("\n======== instead found this: =========\n", .{}); |
| 1536 | // std.debug.warn("{}", .{result}); |
| 1537 | // std.debug.warn("\n======================================\n", .{}); |
| 1538 | // return error.TestFailed; |
| 1539 | // } |
| 1540 | |
| 1541 | // pub fn trim(buf: []const u8) []const u8 { |
| 1542 | // var start: usize = 0; |
| 1543 | // while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} |
| 1544 | |
| 1545 | // var end: usize = buf.len; |
| 1546 | // while (true) { |
| 1547 | // if (end > start) { |
| 1548 | // const new_end = end - 1; |
| 1549 | // if (isWhiteSpace(buf[new_end])) { |
| 1550 | // end = new_end; |
| 1551 | // continue; |
| 1552 | // } |
| 1553 | // } |
| 1554 | // break; |
| 1555 | // } |
| 1556 | // return buf[start..end]; |
| 1557 | // } |
| 1558 | |
| 1559 | // test "trim" { |
| 1560 | // std.testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); |
| 1561 | // std.testing.expect(mem.eql(u8, "", trim(" "))); |
| 1562 | // std.testing.expect(mem.eql(u8, "", trim(""))); |
| 1563 | // std.testing.expect(mem.eql(u8, "abc", trim(" abc"))); |
| 1564 | // std.testing.expect(mem.eql(u8, "abc", trim("abc "))); |
| 1565 | // } |
| 1566 | |
| 1567 | // pub fn isWhiteSpace(byte: u8) bool { |
| 1568 | // return switch (byte) { |
| 1569 | // ' ', '\t', '\n', '\r' => true, |
| 1570 | // else => false, |
| 1571 | // }; |
| 1572 | // } |
| 1573 | |
| 1574 | // pub fn hexToBytes(out: []u8, input: []const u8) !void { |
| 1575 | // if (out.len * 2 < input.len) |
| 1576 | // return error.InvalidLength; |
| 1577 | |
| 1578 | // var in_i: usize = 0; |
| 1579 | // while (in_i != input.len) : (in_i += 2) { |
| 1580 | // const hi = try charToDigit(input[in_i], 16); |
| 1581 | // const lo = try charToDigit(input[in_i + 1], 16); |
| 1582 | // out[in_i / 2] = (hi << 4) | lo; |
| 1583 | // } |
| 1584 | // } |
| 1585 | |
| 1586 | // test "hexToBytes" { |
| 1587 | // const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; |
| 1588 | // var pb: [32]u8 = undefined; |
| 1589 | // try hexToBytes(pb[0..], test_hex_str); |
| 1590 | // try testFmt(test_hex_str, "{X}", .{pb}); |
| 1591 | // } |
| 1592 | |
| 1593 | // test "formatIntValue with comptime_int" { |
| 1594 | // const value: comptime_int = 123456789123456789; |
| 1595 | |
| 1596 | // var buf = std.ArrayList(u8).init(std.testing.allocator); |
| 1597 | // defer buf.deinit(); |
| 1598 | // try formatIntValue(value, "", FormatOptions{}, &buf, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice); |
| 1599 | // std.testing.expect(mem.eql(u8, buf.toSliceConst(), "123456789123456789")); |
| 1600 | // } |
| 1601 | |
| 1602 | // test "formatType max_depth" { |
| 1603 | // const Vec2 = struct { |
| 1604 | // const SelfType = @This(); |
| 1605 | // x: f32, |
| 1606 | // y: f32, |
| 1607 | |
| 1608 | // pub fn format( |
| 1609 | // self: SelfType, |
| 1610 | // comptime fmt: []const u8, |
| 1611 | // options: FormatOptions, |
| 1612 | // context: var, |
| 1613 | // comptime Errors: type, |
| 1614 | // comptime output: fn (@TypeOf(context), []const u8) !void, |
| 1615 | // ) !void { |
| 1616 | // if (fmt.len == 0) { |
| 1617 | // return std.fmtstream.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y }); |
| 1618 | // } else { |
| 1619 | // @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 1620 | // } |
| 1621 | // } |
| 1622 | // }; |
| 1623 | // const E = enum { |
| 1624 | // One, |
| 1625 | // Two, |
| 1626 | // Three, |
| 1627 | // }; |
| 1628 | // const TU = union(enum) { |
| 1629 | // const SelfType = @This(); |
| 1630 | // float: f32, |
| 1631 | // int: u32, |
| 1632 | // ptr: ?*SelfType, |
| 1633 | // }; |
| 1634 | // const S = struct { |
| 1635 | // const SelfType = @This(); |
| 1636 | // a: ?*SelfType, |
| 1637 | // tu: TU, |
| 1638 | // e: E, |
| 1639 | // vec: Vec2, |
| 1640 | // }; |
| 1641 | |
| 1642 | // var inst = S{ |
| 1643 | // .a = null, |
| 1644 | // .tu = TU{ .ptr = null }, |
| 1645 | // .e = E.Two, |
| 1646 | // .vec = Vec2{ .x = 10.2, .y = 2.22 }, |
| 1647 | // }; |
| 1648 | // inst.a = &inst; |
| 1649 | // inst.tu.ptr = &inst.tu; |
| 1650 | |
| 1651 | // var buf0 = std.ArrayList(u8).init(std.testing.allocator); |
| 1652 | // defer buf0.deinit(); |
| 1653 | // try formatType(inst, "", FormatOptions{}, &buf0, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 0); |
| 1654 | // std.testing.expect(mem.eql(u8, buf0.toSlice(), "S{ ... }")); |
| 1655 | |
| 1656 | // var buf1 = std.ArrayList(u8).init(std.testing.allocator); |
| 1657 | // defer buf1.deinit(); |
| 1658 | // try formatType(inst, "", FormatOptions{}, &buf1, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 1); |
| 1659 | // std.testing.expect(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1660 | |
| 1661 | // var buf2 = std.ArrayList(u8).init(std.testing.allocator); |
| 1662 | // defer buf2.deinit(); |
| 1663 | // try formatType(inst, "", FormatOptions{}, &buf2, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 2); |
| 1664 | // std.testing.expect(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1665 | |
| 1666 | // var buf3 = std.ArrayList(u8).init(std.testing.allocator); |
| 1667 | // defer buf3.deinit(); |
| 1668 | // try formatType(inst, "", FormatOptions{}, &buf3, @TypeOf(std.ArrayList(u8).appendSlice).ReturnType.ErrorSet, std.ArrayList(u8).appendSlice, 3); |
| 1669 | // std.testing.expect(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1670 | // } |
| 1671 | |
| 1672 | // test "positional" { |
| 1673 | // try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); |
| 1674 | // try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); |
| 1675 | // try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); |
| 1676 | // try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); |
| 1677 | // try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); |
| 1678 | // } |
| 1679 | |
| 1680 | // test "positional with specifier" { |
| 1681 | // try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); |
| 1682 | // } |
| 1683 | |
| 1684 | // test "positional/alignment/width/precision" { |
| 1685 | // try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); |
| 1686 | // } |
| 1687 | |
| 1688 | // test "vector" { |
| 1689 | // // https://github.com/ziglang/zig/issues/3317 |
| 1690 | // if (builtin.arch == .mipsel) return error.SkipZigTest; |
| 1691 | |
| 1692 | // const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; |
| 1693 | // const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; |
| 1694 | // const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; |
| 1695 | |
| 1696 | // try testFmt("{ true, false, true, false }", "{}", .{vbool}); |
| 1697 | // try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); |
| 1698 | // try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64}); |
| 1699 | // try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); |
| 1700 | // try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); |
| 1701 | // try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); |
| 1702 | // try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); |
| 1703 | // } |
| 1704 | |
| 1705 | // test "enum-literal" { |
| 1706 | // try testFmt(".hello_world", "{}", .{.hello_world}); |
| 1707 | // } |