printValue
Asserts buffer capacity of at least 2 if value is a union.
Function parameters
Parameters
- w:*Writer
- fmt:[]const u8
- options:std.fmt.Options
- value:anytype
- max_depth:usize
Type definitions in this namespace
Types
- Allocating
- Maintains `Writer` state such that it writes to the unused capacity of an
Writes to `buffer` and returns `error.WriteFailed` when it is full.
Functions
- fixed
- Writes to `buffer` and returns `error.WriteFailed` when it is full.
- buffered
- Returns the contents not yet drained.
- writeVec
- If the total number of bytes of `data` fits inside `unusedCapacitySlice`,
- writeSplat
- If the number of bytes to write based on `data` and `splat` fits inside
- writeSplatHeader
- Returns how many bytes were consumed from `header` and `data`.
- writeSplatHeaderLimit
- Equivalent to `writeSplatHeader` but writes at most `limit` bytes.
- flush
- Drains all remaining buffered data.
- defaultFlush
- Repeatedly calls `VTable.drain` until `end` is zero.
- noopFlush
- Does nothing.
- writableArray
- Asserts the provided buffer has total capacity enough for `len`.
- writableSlice
- Asserts the provided buffer has total capacity enough for `len`.
- writableSliceGreedy
- Asserts the provided buffer has total capacity enough for `minimum_len`.
- writableSliceGreedyPreserve
- Asserts the provided buffer has total capacity enough for `minimum_len`
- writableSlicePreserve
- Asserts the provided buffer has total capacity enough for `len`.
- advance
- After calling `writableSliceGreedy`, this function tracks how many bytes
- writeVecAll
- The `data` parameter is mutable because this function needs to mutate the
- writeSplatAll
- The `data` parameter is mutable because this function needs to mutate the
- writeAll
- Calls `drain` as many times as necessary such that all of `bytes` are
- Renders fmt string with args, calling `writer` with slices of bytes.
- writeByte
- Calls `drain` as many times as necessary such that `byte` is transferred.
- writeBytePreserve
- When draining the buffer, ensures that at least `preserve` bytes
- splatByteAll
- Writes the same byte many times, performing the underlying write call as
- splatByte
- Writes the same byte many times, allowing short writes.
- splatBytesAll
- Writes the same slice many times, performing the underlying write call as
- splatBytes
- Writes the same slice many times, allowing short writes.
- writeInt
- Asserts the `buffer` was initialized with a capacity of at least `@sizeOf(T)` bytes.
- writeStruct
- The function is inline to avoid the dead code in case `endian` is
- sendFile
- Unlike `writeSplat` and `writeVec`, this function will call into `VTable`
- sendFileHeader
- Returns how many bytes from `header` and `file_reader` were consumed.
- sendFileReading
- Asserts nonzero buffer capacity and nonzero `limit`.
- sendFileAll
- Number of bytes logically written is returned.
- sendFileReadingAll
- Equivalent to `sendFileAll` but uses direct `pread` and `read` calls on
- printValue
- Asserts `buffer` capacity of at least 2 if `value` is a union.
- printIntAny
- In general, prefer `printInt` to avoid generic explosion.
- printFloat
- Uses a larger stack buffer; asserts mode is decimal or scientific.
- printFloatHexOptions
- Uses a smaller stack buffer; asserts mode is not decimal or scientific.
- printByteSize
- Format option `precision` is ignored when `value` is less than 1kB
- printDuration
- Writes number of nanoseconds according to its signed magnitude:
- writeUleb128
- Write a single unsigned integer as LEB128 to the given writer.
- writeSleb128
- Write a single signed integer as LEB128 to the given writer.
- writeLeb128
- Write a single integer as LEB128 to the given writer.
- consume
- Removes the first `n` bytes from `buffer` by shifting buffer contents,
- consumeAll
- Shortcut for setting `end` to zero and returning zero.
- unimplementedSendFile
- For use when the `Writer` implementation can cannot offer a more efficient
- fixedDrain
- When this function is called it usually means the buffer got full, so it's
- Hashed
- Provides a `Writer` implementation based on calling `Hasher.update`, sending
- Hashing
- Provides a `Writer` implementation based on calling `Hasher.update`,
Error sets in this namespace
Error Sets
= .{ .vtable = &.{ .drain = failingDrain, .sendFile = failingSendFile, .rebase = failingRebase, }, .buffer = &.{}, }
Values
- failing
- = .{ .vtable = &.{ .drain = failingDrain, .sendFile = failingSendFile, .rebase = failingRebase, }, .buffer = &.{}, }
Source
Implementation
pub fn printValue(
w: *Writer,
comptime fmt: []const u8,
options: std.fmt.Options,
value: anytype,
max_depth: usize,
) Error!void {
const T = @TypeOf(value);
switch (fmt.len) {
1 => switch (fmt[0]) {
'*' => return w.printAddress(value),
'f' => return value.format(w),
'd' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)),
.int, .comptime_int => return printInt(w, value, 10, .lower, options),
.@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)),
.@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options),
.vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value),
},
'c' => return w.printAsciiChar(value, options),
'u' => return w.printUnicodeCodepoint(value),
'b' => switch (@typeInfo(T)) {
.int, .comptime_int => return printInt(w, value, 2, .lower, options),
.@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options),
.@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)),
.vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value),
},
'o' => switch (@typeInfo(T)) {
.int, .comptime_int => return printInt(w, value, 8, .lower, options),
.@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options),
.@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)),
.vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value),
},
'x' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
.int, .comptime_int => return printInt(w, value, 16, .lower, options),
.@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options),
.@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)),
.pointer => |info| switch (info.size) {
.one, .slice => {
const slice: []const u8 = value;
optionsForbidden(options);
return printHex(w, slice, .lower);
},
.many, .c => {
const slice: [:0]const u8 = std.mem.span(value);
optionsForbidden(options);
return printHex(w, slice, .lower);
},
},
.array => {
const slice: []const u8 = &value;
optionsForbidden(options);
return printHex(w, slice, .lower);
},
.vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value),
},
'X' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .upper)),
.int, .comptime_int => return printInt(w, value, 16, .upper, options),
.@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options),
.@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)),
.pointer => |info| switch (info.size) {
.one, .slice => {
const slice: []const u8 = value;
optionsForbidden(options);
return printHex(w, slice, .upper);
},
.many, .c => {
const slice: [:0]const u8 = std.mem.span(value);
optionsForbidden(options);
return printHex(w, slice, .upper);
},
},
.array => {
const slice: []const u8 = &value;
optionsForbidden(options);
return printHex(w, slice, .upper);
},
.vector => return printVector(w, fmt, options, value, max_depth),
else => invalidFmtError(fmt, value),
},
's' => switch (@typeInfo(T)) {
.pointer => |info| switch (info.size) {
.one, .slice => {
const slice: []const u8 = value;
return w.alignBufferOptions(slice, options);
},
.many, .c => {
const slice: [:0]const u8 = std.mem.span(value);
return w.alignBufferOptions(slice, options);
},
},
.array => {
const slice: []const u8 = &value;
return w.alignBufferOptions(slice, options);
},
else => invalidFmtError(fmt, value),
},
'B' => switch (@typeInfo(T)) {
.int, .comptime_int => return w.printByteSize(value, .decimal, options),
.@"struct" => return value.formatByteSize(w, .decimal),
else => invalidFmtError(fmt, value),
},
'D' => switch (@typeInfo(T)) {
.int, .comptime_int => return w.printDuration(value, options),
.@"struct" => return value.formatDuration(w),
else => invalidFmtError(fmt, value),
},
'e' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
.@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
else => invalidFmtError(fmt, value),
},
'E' => switch (@typeInfo(T)) {
.float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)),
.@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)),
else => invalidFmtError(fmt, value),
},
't' => switch (@typeInfo(T)) {
.error_set => return w.alignBufferOptions(@errorName(value), options),
.@"enum", .@"union" => return w.alignBufferOptions(@tagName(value), options),
else => invalidFmtError(fmt, value),
},
else => {},
},
2 => switch (fmt[0]) {
'B' => switch (fmt[1]) {
'i' => switch (@typeInfo(T)) {
.int, .comptime_int => return w.printByteSize(value, .binary, options),
.@"struct" => return value.formatByteSize(w, .binary),
else => invalidFmtError(fmt, value),
},
else => {},
},
else => {},
},
3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) {
.pointer => |info| switch (info.size) {
.one, .slice => {
const slice: []const u8 = value;
optionsForbidden(options);
return w.printBase64(slice);
},
.many, .c => {
const slice: [:0]const u8 = std.mem.span(value);
optionsForbidden(options);
return w.printBase64(slice);
},
},
.array => {
const slice: []const u8 = &value;
optionsForbidden(options);
return w.printBase64(slice);
},
else => invalidFmtError(fmt, value),
},
else => {},
}
const is_any = comptime std.mem.eql(u8, fmt, ANY);
if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) {
// after 0.15.0 is tagged, delete this compile error and its condition
@compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
}
switch (@typeInfo(T)) {
.float, .comptime_float => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return printFloat(w, value, options.toNumber(.decimal, .lower));
},
.int, .comptime_int => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return printInt(w, value, 10, .lower, options);
},
.bool => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
const string: []const u8 = if (value) "true" else "false";
return w.alignBufferOptions(string, options);
},
.void => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return w.alignBufferOptions("void", options);
},
.optional => {
const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '?')
stripOptionalOrErrorUnionSpec(fmt)
else if (is_any)
ANY
else
@compileError("cannot print optional without a specifier (i.e. {?} or {any})");
if (value) |payload| {
return w.printValue(remaining_fmt, options, payload, max_depth);
} else {
return w.alignBufferOptions("null", options);
}
},
.error_union => {
const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '!')
stripOptionalOrErrorUnionSpec(fmt)
else if (is_any)
ANY
else
@compileError("cannot print error union without a specifier (i.e. {!} or {any})");
if (value) |payload| {
return w.printValue(remaining_fmt, options, payload, max_depth);
} else |err| {
return w.printValue("", options, err, max_depth);
}
},
.error_set => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
optionsForbidden(options);
return printErrorSet(w, value);
},
.@"enum" => |info| {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
optionsForbidden(options);
if (info.is_exhaustive) {
return printEnumExhaustive(w, value);
} else {
return printEnumNonexhaustive(w, value);
}
},
.@"union" => |info| {
if (!is_any) {
if (fmt.len != 0) invalidFmtError(fmt, value);
return printValue(w, ANY, options, value, max_depth);
}
if (max_depth == 0) {
try w.writeAll(".{ ... }");
return;
}
if (info.tag_type) |UnionTagType| {
try w.writeAll(".{ .");
try w.writeAll(@tagName(@as(UnionTagType, value)));
try w.writeAll(" = ");
inline for (info.fields) |u_field| {
if (value == @field(UnionTagType, u_field.name)) {
try w.printValue(ANY, options, @field(value, u_field.name), max_depth - 1);
}
}
try w.writeAll(" }");
} else switch (info.layout) {
.auto => {
return w.writeAll(".{ ... }");
},
.@"extern", .@"packed" => {
if (info.fields.len == 0) return w.writeAll(".{}");
try w.writeAll(".{ ");
inline for (info.fields, 1..) |field, i| {
try w.writeByte('.');
try w.writeAll(field.name);
try w.writeAll(" = ");
try w.printValue(ANY, options, @field(value, field.name), max_depth - 1);
try w.writeAll(if (i < info.fields.len) ", " else " }");
}
},
}
},
.@"struct" => |info| {
if (!is_any) {
if (fmt.len != 0) invalidFmtError(fmt, value);
return printValue(w, ANY, options, value, max_depth);
}
if (info.is_tuple) {
// Skip the type and field names when formatting tuples.
if (max_depth == 0) {
try w.writeAll(".{ ... }");
return;
}
try w.writeAll(".{");
inline for (info.fields, 0..) |f, i| {
if (i == 0) {
try w.writeAll(" ");
} else {
try w.writeAll(", ");
}
try w.printValue(ANY, options, @field(value, f.name), max_depth - 1);
}
try w.writeAll(" }");
return;
}
if (max_depth == 0) {
try w.writeAll(".{ ... }");
return;
}
try w.writeAll(".{");
inline for (info.fields, 0..) |f, i| {
if (i == 0) {
try w.writeAll(" .");
} else {
try w.writeAll(", .");
}
try w.writeAll(f.name);
try w.writeAll(" = ");
try w.printValue(ANY, options, @field(value, f.name), max_depth - 1);
}
try w.writeAll(" }");
},
.pointer => |ptr_info| switch (ptr_info.size) {
.one => switch (@typeInfo(ptr_info.child)) {
.array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth),
.@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth),
else => {
var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
try w.writeVecAll(&buffers);
try w.printInt(@intFromPtr(value), 16, .lower, options);
return;
},
},
.many, .c => {
if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
optionsForbidden(options);
try w.printAddress(value);
},
.slice => {
if (!is_any)
@compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");
if (max_depth == 0) return w.writeAll("{ ... }");
try w.writeAll("{ ");
for (value, 0..) |elem, i| {
try w.printValue(fmt, options, elem, max_depth - 1);
if (i != value.len - 1) {
try w.writeAll(", ");
}
}
try w.writeAll(" }");
},
},
.array => {
if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})");
if (max_depth == 0) return w.writeAll("{ ... }");
try w.writeAll("{ ");
for (value, 0..) |elem, i| {
try w.printValue(fmt, options, elem, max_depth - 1);
if (i < value.len - 1) {
try w.writeAll(", ");
}
}
try w.writeAll(" }");
},
.vector => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return printVector(w, fmt, options, value, max_depth);
},
.@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),
.type => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return w.alignBufferOptions(@typeName(value), options);
},
.enum_literal => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
optionsForbidden(options);
var vecs: [2][]const u8 = .{ ".", @tagName(value) };
return w.writeVecAll(&vecs);
},
.null => {
if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
return w.alignBufferOptions("null", options);
},
else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"),
}
}