Skip to content

Commit a4c5b07

Browse files
committed
std.mem: Add packedArrayByteLen function (and use it)
This is a helper function intended to make using writePackedInt/readPackedInt to write to/read from packed arrays a bit less error prone. The math involved is easy, but a naive implementation will overflow when calculating a valid length. For example: std.math.divCeil(usize, @bitSizeOf(u4) * some_slice.len, byte_size_in_bits) This will overflow during the multiplication if `some_slice.len` is > `maxInt(usize) / 4 + 1`, even though the final calculated byte count would be able to fit in a `usize`. When using this newly added function, `error.Overflow` is returned only when the *result* can't fit in a `usize`.
1 parent f296eec commit a4c5b07

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

lib/compiler/aro/aro/Preprocessor.zig

+2-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,8 @@ fn preprocessExtra(pp: *Preprocessor, source: Source) MacroError!TokenWithExpans
389389
try pp.ensureTotalTokenCapacity(pp.tokens.len + estimated_token_count);
390390

391391
var if_level: u8 = 0;
392-
var if_kind: [64]u8 = .{0} ** 64;
392+
const if_kind_buf_size = comptime mem.packedArrayByteLen(u2, 256) catch unreachable;
393+
var if_kind: [if_kind_buf_size]u8 = .{0} ** if_kind_buf_size;
393394
const until_else = 0;
394395
const until_endif = 1;
395396
const until_endif_seen_else = 2;

lib/std/mem.zig

+38
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,44 @@ test writeVarPackedInt {
21312131
try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st);
21322132
}
21332133

2134+
/// Returns the minimum number of bytes needed to store `num_elements`
2135+
/// of `T` without any padding bits between elements.
2136+
/// Uses `@bitSizeOf` to determine the width of `T`.
2137+
pub fn packedArrayByteLen(comptime T: type, num_elements: usize) error{Overflow}!usize {
2138+
if (@bitSizeOf(T) == 0) return 0;
2139+
const extra_bits_needed = std.math.log2(@bitSizeOf(T) - 1) + 1;
2140+
const IntermediateInt = std.meta.Int(.unsigned, @bitSizeOf(usize) + extra_bits_needed);
2141+
const total_bits = @bitSizeOf(T) * @as(IntermediateInt, num_elements);
2142+
const byte_len = std.math.divCeil(IntermediateInt, total_bits, byte_size_in_bits) catch unreachable;
2143+
return std.math.cast(usize, byte_len) orelse error.Overflow;
2144+
}
2145+
2146+
test packedArrayByteLen {
2147+
try std.testing.expectEqual(0, try packedArrayByteLen(u0, std.math.maxInt(usize)));
2148+
try std.testing.expectEqual(5857, try packedArrayByteLen(u21, 2231));
2149+
try std.testing.expectEqual(5857, comptime packedArrayByteLen(u21, 2231) catch unreachable);
2150+
const S = packed struct(u21) {
2151+
a: u17,
2152+
b: u4,
2153+
};
2154+
try std.testing.expectEqual(5857, try packedArrayByteLen(S, 2231));
2155+
try std.testing.expectEqual(5857, comptime packedArrayByteLen(S, 2231) catch unreachable);
2156+
2157+
try std.testing.expectEqual(std.math.divCeil(usize, std.math.maxInt(usize), 2), try packedArrayByteLen(u4, std.math.maxInt(usize)));
2158+
try std.testing.expectEqual(switch (@bitSizeOf(usize)) {
2159+
32 => 3758096384,
2160+
64 => 16140901064495857664,
2161+
else => @compileError("unsupported target"),
2162+
}, try packedArrayByteLen(u7, std.math.maxInt(usize)));
2163+
try std.testing.expectEqual(std.math.maxInt(usize), try packedArrayByteLen(u8, std.math.maxInt(usize)));
2164+
try std.testing.expectError(error.Overflow, packedArrayByteLen(u9, std.math.maxInt(usize)));
2165+
2166+
const HalfSize = std.meta.Int(.unsigned, @bitSizeOf(usize) / 2);
2167+
const ExtremelyLargeType = [std.math.maxInt(HalfSize) + 2]u8;
2168+
try std.testing.expectEqual(std.math.maxInt(usize), packedArrayByteLen(ExtremelyLargeType, std.math.maxInt(HalfSize)));
2169+
try std.testing.expectEqual(error.Overflow, packedArrayByteLen(ExtremelyLargeType, std.math.maxInt(HalfSize) + 1));
2170+
}
2171+
21342172
/// Swap the byte order of all the members of the fields of a struct
21352173
/// (Changing their endianness)
21362174
pub fn byteSwapAllFields(comptime S: type, ptr: *S) void {

0 commit comments

Comments
 (0)