| ... | @@ -136,6 +136,15 @@ pub fn writableSliceGreedy(bw: *BufferedWriter, minimum_length: usize) Writer.Er | ... | @@ -136,6 +136,15 @@ pub fn writableSliceGreedy(bw: *BufferedWriter, minimum_length: usize) Writer.Er |
| 136 | return bw.buffer[bw.end..]; | 136 | return bw.buffer[bw.end..]; |
| 137 | } | 137 | } |
| 138 | | 138 | |
| | 139 | pub fn ensureUnusedCapacity(bw: *BufferedWriter, n: usize) Writer.Error!void { |
| | 140 | _ = try writableSliceGreedy(bw, n); |
| | 141 | } |
| | 142 | |
| | 143 | pub fn undo(bw: *BufferedWriter, n: usize) void { |
| | 144 | bw.end -= n; |
| | 145 | bw.count -= n; |
| | 146 | } |
| | 147 | |
| 139 | /// After calling `writableSliceGreedy`, this function tracks how many bytes | 148 | /// After calling `writableSliceGreedy`, this function tracks how many bytes |
| 140 | /// were written to it. | 149 | /// were written to it. |
| 141 | /// | 150 | /// |
| ... | @@ -797,18 +806,13 @@ pub fn printValue( | ... | @@ -797,18 +806,13 @@ pub fn printValue( |
| 797 | max_depth: usize, | 806 | max_depth: usize, |
| 798 | ) Writer.Error!void { | 807 | ) Writer.Error!void { |
| 799 | const T = @TypeOf(value); | 808 | const T = @TypeOf(value); |
| 800 | const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) | 809 | |
| 801 | defaultFormatString(T) | 810 | if (comptime std.mem.eql(u8, fmt, "*")) { |
| 802 | else if (fmt.len != 0 and (fmt[0] == '?' or fmt[0] == '!')) switch (@typeInfo(T)) { | | |
| 803 | .optional, .error_union => fmt, | | |
| 804 | else => stripOptionalOrErrorUnionSpec(fmt), | | |
| 805 | } else fmt; | | |
| 806 | | | |
| 807 | if (comptime std.mem.eql(u8, actual_fmt, "*")) { | | |
| 808 | return bw.printAddress(value); | 811 | return bw.printAddress(value); |
| 809 | } | 812 | } |
| 810 | | 813 | |
| 811 | if (std.meta.hasMethod(T, "format")) { | 814 | const is_any = comptime std.mem.eql(u8, fmt, ANY); |
| | 815 | if (!is_any and std.meta.hasMethod(T, "format")) { |
| 812 | if (fmt.len > 0 and fmt[0] == 'f') { | 816 | if (fmt.len > 0 and fmt[0] == 'f') { |
| 813 | return value.format(bw, fmt[1..]); | 817 | return value.format(bw, fmt[1..]); |
| 814 | } else if (fmt.len == 0) { | 818 | } else if (fmt.len == 0) { |
| ... | @@ -818,20 +822,23 @@ pub fn printValue( | ... | @@ -818,20 +822,23 @@ pub fn printValue( |
| 818 | } | 822 | } |
| 819 | | 823 | |
| 820 | switch (@typeInfo(T)) { | 824 | switch (@typeInfo(T)) { |
| 821 | .float, .comptime_float => return bw.printFloat(actual_fmt, options, value), | 825 | .float, .comptime_float => return bw.printFloat(if (is_any) "d" else fmt, options, value), |
| 822 | .int, .comptime_int => return bw.printInt(actual_fmt, options, value), | 826 | .int, .comptime_int => return bw.printInt(if (is_any) "d" else fmt, options, value), |
| 823 | .bool => { | 827 | .bool => { |
| 824 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 828 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 825 | return bw.alignBufferOptions(if (value) "true" else "false", options); | 829 | return bw.alignBufferOptions(if (value) "true" else "false", options); |
| 826 | }, | 830 | }, |
| 827 | .void => { | 831 | .void => { |
| 828 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 832 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 829 | return bw.alignBufferOptions("void", options); | 833 | return bw.alignBufferOptions("void", options); |
| 830 | }, | 834 | }, |
| 831 | .optional => { | 835 | .optional => { |
| 832 | if (actual_fmt.len == 0 or actual_fmt[0] != '?') | 836 | const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '?') |
| | 837 | stripOptionalOrErrorUnionSpec(fmt) |
| | 838 | else if (is_any) |
| | 839 | ANY |
| | 840 | else |
| 833 | @compileError("cannot print optional without a specifier (i.e. {?} or {any})"); | 841 | @compileError("cannot print optional without a specifier (i.e. {?} or {any})"); |
| 834 | const remaining_fmt = comptime stripOptionalOrErrorUnionSpec(actual_fmt); | | |
| 835 | if (value) |payload| { | 842 | if (value) |payload| { |
| 836 | return bw.printValue(remaining_fmt, options, payload, max_depth); | 843 | return bw.printValue(remaining_fmt, options, payload, max_depth); |
| 837 | } else { | 844 | } else { |
| ... | @@ -839,9 +846,12 @@ pub fn printValue( | ... | @@ -839,9 +846,12 @@ pub fn printValue( |
| 839 | } | 846 | } |
| 840 | }, | 847 | }, |
| 841 | .error_union => { | 848 | .error_union => { |
| 842 | if (actual_fmt.len == 0 or actual_fmt[0] != '!') | 849 | const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '!') |
| 843 | @compileError("cannot format error union without a specifier (i.e. {!} or {any})"); | 850 | stripOptionalOrErrorUnionSpec(fmt) |
| 844 | const remaining_fmt = comptime stripOptionalOrErrorUnionSpec(actual_fmt); | 851 | else if (is_any) |
| | 852 | ANY |
| | 853 | else |
| | 854 | @compileError("cannot print error union without a specifier (i.e. {!} or {any})"); |
| 845 | if (value) |payload| { | 855 | if (value) |payload| { |
| 846 | return bw.printValue(remaining_fmt, options, payload, max_depth); | 856 | return bw.printValue(remaining_fmt, options, payload, max_depth); |
| 847 | } else |err| { | 857 | } else |err| { |
| ... | @@ -849,40 +859,43 @@ pub fn printValue( | ... | @@ -849,40 +859,43 @@ pub fn printValue( |
| 849 | } | 859 | } |
| 850 | }, | 860 | }, |
| 851 | .error_set => { | 861 | .error_set => { |
| 852 | if (actual_fmt.len > 0 and actual_fmt[0] == 's') { | 862 | if (fmt.len == 1 and fmt[0] == 's') return bw.writeAll(@errorName(value)); |
| 853 | return bw.writeAll(@errorName(value)); | 863 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 854 | } else if (actual_fmt.len != 0) { | 864 | try printErrorSet(bw, value); |
| 855 | invalidFmtError(fmt, value); | | |
| 856 | } else { | | |
| 857 | try bw.writeAll("error."); | | |
| 858 | try bw.writeAll(@errorName(value)); | | |
| 859 | } | | |
| 860 | }, | 865 | }, |
| 861 | .@"enum" => |enum_info| { | 866 | .@"enum" => { |
| 862 | try bw.writeAll(@typeName(T)); | 867 | if (fmt.len == 1 and fmt[0] == 's') { |
| 863 | if (enum_info.is_exhaustive) { | | |
| 864 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | | |
| 865 | try bw.writeAll("."); | | |
| 866 | try bw.writeAll(@tagName(value)); | 868 | try bw.writeAll(@tagName(value)); |
| 867 | return; | 869 | return; |
| 868 | } | 870 | } |
| 869 | | 871 | if (!is_any) { |
| 870 | // Use @tagName only if value is one of known fields | 872 | if (fmt.len != 0) return printValue(bw, fmt, options, @intFromEnum(value), max_depth); |
| | 873 | return printValue(bw, ANY, options, value, max_depth); |
| | 874 | } |
| | 875 | const enum_info = @typeInfo(T).@"enum"; |
| | 876 | if (enum_info.is_exhaustive) { |
| | 877 | var vecs: [3][]const u8 = .{ @typeName(T), ".", @tagName(value) }; |
| | 878 | try bw.writeVecAll(&vecs); |
| | 879 | return; |
| | 880 | } |
| | 881 | try bw.writeAll(@typeName(T)); |
| 871 | @setEvalBranchQuota(3 * enum_info.fields.len); | 882 | @setEvalBranchQuota(3 * enum_info.fields.len); |
| 872 | inline for (enum_info.fields) |enumField| { | 883 | inline for (enum_info.fields) |field| { |
| 873 | if (@intFromEnum(value) == enumField.value) { | 884 | if (@intFromEnum(value) == field.value) { |
| 874 | try bw.writeAll("."); | 885 | try bw.writeAll("."); |
| 875 | try bw.writeAll(@tagName(value)); | 886 | try bw.writeAll(@tagName(value)); |
| 876 | return; | 887 | return; |
| 877 | } | 888 | } |
| 878 | } | 889 | } |
| 879 | | | |
| 880 | try bw.writeByte('('); | 890 | try bw.writeByte('('); |
| 881 | try bw.printValue(actual_fmt, options, @intFromEnum(value), max_depth); | 891 | try bw.printValue(ANY, options, @intFromEnum(value), max_depth); |
| 882 | try bw.writeByte(')'); | 892 | try bw.writeByte(')'); |
| 883 | }, | 893 | }, |
| 884 | .@"union" => |info| { | 894 | .@"union" => |info| { |
| 885 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 895 | if (!is_any) { |
| | 896 | if (fmt.len != 0) invalidFmtError(fmt, value); |
| | 897 | return printValue(bw, ANY, options, value, max_depth); |
| | 898 | } |
| 886 | try bw.writeAll(@typeName(T)); | 899 | try bw.writeAll(@typeName(T)); |
| 887 | if (max_depth == 0) { | 900 | if (max_depth == 0) { |
| 888 | try bw.writeAll("{ ... }"); | 901 | try bw.writeAll("{ ... }"); |
| ... | @@ -904,7 +917,10 @@ pub fn printValue( | ... | @@ -904,7 +917,10 @@ pub fn printValue( |
| 904 | } | 917 | } |
| 905 | }, | 918 | }, |
| 906 | .@"struct" => |info| { | 919 | .@"struct" => |info| { |
| 907 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 920 | if (!is_any) { |
| | 921 | if (fmt.len != 0) invalidFmtError(fmt, value); |
| | 922 | return printValue(bw, ANY, options, value, max_depth); |
| | 923 | } |
| 908 | if (info.is_tuple) { | 924 | if (info.is_tuple) { |
| 909 | // Skip the type and field names when formatting tuples. | 925 | // Skip the type and field names when formatting tuples. |
| 910 | if (max_depth == 0) { | 926 | if (max_depth == 0) { |
| ... | @@ -944,7 +960,7 @@ pub fn printValue( | ... | @@ -944,7 +960,7 @@ pub fn printValue( |
| 944 | .pointer => |ptr_info| switch (ptr_info.size) { | 960 | .pointer => |ptr_info| switch (ptr_info.size) { |
| 945 | .one => switch (@typeInfo(ptr_info.child)) { | 961 | .one => switch (@typeInfo(ptr_info.child)) { |
| 946 | .array, .@"enum", .@"union", .@"struct" => { | 962 | .array, .@"enum", .@"union", .@"struct" => { |
| 947 | return bw.printValue(actual_fmt, options, value.*, max_depth); | 963 | return bw.printValue(fmt, options, value.*, max_depth); |
| 948 | }, | 964 | }, |
| 949 | else => { | 965 | else => { |
| 950 | var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; | 966 | var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; |
| ... | @@ -954,37 +970,36 @@ pub fn printValue( | ... | @@ -954,37 +970,36 @@ pub fn printValue( |
| 954 | }, | 970 | }, |
| 955 | }, | 971 | }, |
| 956 | .many, .c => { | 972 | .many, .c => { |
| 957 | if (actual_fmt.len == 0) | 973 | if (ptr_info.sentinel() != null) |
| 958 | @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); | 974 | return bw.printValue(fmt, options, std.mem.span(value), max_depth); |
| 959 | if (ptr_info.sentinel() != null) { | 975 | if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8) |
| 960 | return bw.printValue(actual_fmt, options, std.mem.span(value), max_depth); | | |
| 961 | } | | |
| 962 | if (actual_fmt[0] == 's' and ptr_info.child == u8) { | | |
| 963 | return bw.alignBufferOptions(std.mem.span(value), options); | 976 | return bw.alignBufferOptions(std.mem.span(value), options); |
| 964 | } | 977 | if (!is_any and fmt.len == 0) |
| 965 | invalidFmtError(fmt, value); | 978 | @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); |
| | 979 | if (!is_any and fmt.len != 0) |
| | 980 | invalidFmtError(fmt, value); |
| | 981 | try bw.printAddress(value); |
| 966 | }, | 982 | }, |
| 967 | .slice => { | 983 | .slice => { |
| 968 | if (actual_fmt.len == 0) | 984 | if (!is_any and fmt.len == 0) |
| 969 | @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})"); | 985 | @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})"); |
| 970 | if (max_depth == 0) { | 986 | if (max_depth == 0) |
| 971 | return bw.writeAll("{ ... }"); | 987 | return bw.writeAll("{ ... }"); |
| 972 | } | 988 | if (ptr_info.child == u8) switch (fmt.len) { |
| 973 | if (ptr_info.child == u8) switch (actual_fmt.len) { | 989 | 1 => switch (fmt[0]) { |
| 974 | 1 => switch (actual_fmt[0]) { | | |
| 975 | 's' => return bw.alignBufferOptions(value, options), | 990 | 's' => return bw.alignBufferOptions(value, options), |
| 976 | 'x' => return bw.printHex(value, .lower), | 991 | 'x' => return bw.printHex(value, .lower), |
| 977 | 'X' => return bw.printHex(value, .upper), | 992 | 'X' => return bw.printHex(value, .upper), |
| 978 | else => {}, | 993 | else => {}, |
| 979 | }, | 994 | }, |
| 980 | 3 => if (actual_fmt[0] == 'b' and actual_fmt[1] == '6' and actual_fmt[2] == '4') { | 995 | 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') { |
| 981 | return bw.printBase64(value); | 996 | return bw.printBase64(value); |
| 982 | }, | 997 | }, |
| 983 | else => {}, | 998 | else => {}, |
| 984 | }; | 999 | }; |
| 985 | try bw.writeAll("{ "); | 1000 | try bw.writeAll("{ "); |
| 986 | for (value, 0..) |elem, i| { | 1001 | for (value, 0..) |elem, i| { |
| 987 | try bw.printValue(actual_fmt, options, elem, max_depth - 1); | 1002 | try bw.printValue(fmt, options, elem, max_depth - 1); |
| 988 | if (i != value.len - 1) { | 1003 | if (i != value.len - 1) { |
| 989 | try bw.writeAll(", "); | 1004 | try bw.writeAll(", "); |
| 990 | } | 1005 | } |
| ... | @@ -993,23 +1008,23 @@ pub fn printValue( | ... | @@ -993,23 +1008,23 @@ pub fn printValue( |
| 993 | }, | 1008 | }, |
| 994 | }, | 1009 | }, |
| 995 | .array => |info| { | 1010 | .array => |info| { |
| 996 | if (actual_fmt.len == 0) | 1011 | if (fmt.len == 0) |
| 997 | @compileError("cannot format array without a specifier (i.e. {s} or {any})"); | 1012 | @compileError("cannot format array without a specifier (i.e. {s} or {any})"); |
| 998 | if (max_depth == 0) { | 1013 | if (max_depth == 0) { |
| 999 | return bw.writeAll("{ ... }"); | 1014 | return bw.writeAll("{ ... }"); |
| 1000 | } | 1015 | } |
| 1001 | if (info.child == u8) { | 1016 | if (info.child == u8) { |
| 1002 | if (actual_fmt[0] == 's') { | 1017 | if (fmt[0] == 's') { |
| 1003 | return bw.alignBufferOptions(&value, options); | 1018 | return bw.alignBufferOptions(&value, options); |
| 1004 | } else if (actual_fmt[0] == 'x') { | 1019 | } else if (fmt[0] == 'x') { |
| 1005 | return bw.printHex(&value, .lower); | 1020 | return bw.printHex(&value, .lower); |
| 1006 | } else if (actual_fmt[0] == 'X') { | 1021 | } else if (fmt[0] == 'X') { |
| 1007 | return bw.printHex(&value, .upper); | 1022 | return bw.printHex(&value, .upper); |
| 1008 | } | 1023 | } |
| 1009 | } | 1024 | } |
| 1010 | try bw.writeAll("{ "); | 1025 | try bw.writeAll("{ "); |
| 1011 | for (value, 0..) |elem, i| { | 1026 | for (value, 0..) |elem, i| { |
| 1012 | try bw.printValue(actual_fmt, options, elem, max_depth - 1); | 1027 | try bw.printValue(fmt, options, elem, max_depth - 1); |
| 1013 | if (i < value.len - 1) { | 1028 | if (i < value.len - 1) { |
| 1014 | try bw.writeAll(", "); | 1029 | try bw.writeAll(", "); |
| 1015 | } | 1030 | } |
| ... | @@ -1023,7 +1038,7 @@ pub fn printValue( | ... | @@ -1023,7 +1038,7 @@ pub fn printValue( |
| 1023 | try bw.writeAll("{ "); | 1038 | try bw.writeAll("{ "); |
| 1024 | var i: usize = 0; | 1039 | var i: usize = 0; |
| 1025 | while (i < info.len) : (i += 1) { | 1040 | while (i < info.len) : (i += 1) { |
| 1026 | try bw.printValue(actual_fmt, options, value[i], max_depth - 1); | 1041 | try bw.printValue(fmt, options, value[i], max_depth - 1); |
| 1027 | if (i < info.len - 1) { | 1042 | if (i < info.len - 1) { |
| 1028 | try bw.writeAll(", "); | 1043 | try bw.writeAll(", "); |
| 1029 | } | 1044 | } |
| ... | @@ -1032,22 +1047,27 @@ pub fn printValue( | ... | @@ -1032,22 +1047,27 @@ pub fn printValue( |
| 1032 | }, | 1047 | }, |
| 1033 | .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), | 1048 | .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), |
| 1034 | .type => { | 1049 | .type => { |
| 1035 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 1050 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 1036 | return bw.alignBufferOptions(@typeName(value), options); | 1051 | return bw.alignBufferOptions(@typeName(value), options); |
| 1037 | }, | 1052 | }, |
| 1038 | .enum_literal => { | 1053 | .enum_literal => { |
| 1039 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 1054 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 1040 | const buffer = [_]u8{'.'} ++ @tagName(value); | 1055 | const buffer = [_]u8{'.'} ++ @tagName(value); |
| 1041 | return bw.alignBufferOptions(buffer, options); | 1056 | return bw.alignBufferOptions(buffer, options); |
| 1042 | }, | 1057 | }, |
| 1043 | .null => { | 1058 | .null => { |
| 1044 | if (actual_fmt.len != 0) invalidFmtError(fmt, value); | 1059 | if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); |
| 1045 | return bw.alignBufferOptions("null", options); | 1060 | return bw.alignBufferOptions("null", options); |
| 1046 | }, | 1061 | }, |
| 1047 | else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"), | 1062 | else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"), |
| 1048 | } | 1063 | } |
| 1049 | } | 1064 | } |
| 1050 | | 1065 | |
| | 1066 | fn printErrorSet(bw: *BufferedWriter, error_set: anyerror) Writer.Error!void { |
| | 1067 | var vecs: [2][]const u8 = .{ "error.", @errorName(error_set) }; |
| | 1068 | try bw.writeVecAll(&vecs); |
| | 1069 | } |
| | 1070 | |
| 1051 | pub fn printInt( | 1071 | pub fn printInt( |
| 1052 | bw: *BufferedWriter, | 1072 | bw: *BufferedWriter, |
| 1053 | comptime fmt: []const u8, | 1073 | comptime fmt: []const u8, |
| ... | @@ -1376,24 +1396,6 @@ pub fn printByteSize( | ... | @@ -1376,24 +1396,6 @@ pub fn printByteSize( |
| 1376 | // This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948 | 1396 | // This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948 |
| 1377 | const ANY = "any"; | 1397 | const ANY = "any"; |
| 1378 | | 1398 | |
| 1379 | fn defaultFormatString(comptime T: type) [:0]const u8 { | | |
| 1380 | switch (@typeInfo(T)) { | | |
| 1381 | .array, .vector => return ANY, | | |
| 1382 | .pointer => |ptr_info| switch (ptr_info.size) { | | |
| 1383 | .one => switch (@typeInfo(ptr_info.child)) { | | |
| 1384 | .array => return ANY, | | |
| 1385 | else => {}, | | |
| 1386 | }, | | |
| 1387 | .many, .c => return "*", | | |
| 1388 | .slice => return ANY, | | |
| 1389 | }, | | |
| 1390 | .optional => |info| return "?" ++ defaultFormatString(info.child), | | |
| 1391 | .error_union => |info| return "!" ++ defaultFormatString(info.payload), | | |
| 1392 | else => {}, | | |
| 1393 | } | | |
| 1394 | return ""; | | |
| 1395 | } | | |
| 1396 | | | |
| 1397 | fn stripOptionalOrErrorUnionSpec(comptime fmt: []const u8) []const u8 { | 1399 | fn stripOptionalOrErrorUnionSpec(comptime fmt: []const u8) []const u8 { |
| 1398 | return if (std.mem.eql(u8, fmt[1..], ANY)) | 1400 | return if (std.mem.eql(u8, fmt[1..], ANY)) |
| 1399 | ANY | 1401 | ANY |