authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-11 05:56:08+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-11 05:56:08+02:00
log6ae46c1bc8c55db2207bc32183a62607a80ee642
tree1ec898df93426d8affc1387f0aba3cb292b0ec40
parent41d08843ffd59e0ae1396dbf958bdd11257f384a
parent1042cbe0100ba0fbd8265f975c49356d31e4bd91

Merge pull request 'Fix Build.tryFindProgram, Configuration.load/storeBits and reintroduce test' (#35673) from squeek502/zig:find-program-fixes into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35673 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

3 files changed, 104 insertions(+), 17 deletions(-)

lib/std/Build.zig+7-1
......@@ -1836,10 +1836,14 @@ fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 {
18361836 if (b.graph.environ_map.get("PATHEXT")) |PATHEXT| {
18371837 var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter);
18381838
1839 const extended_path_buf = arena.alloc(u8, full_path.len + 1 + std.process.WindowsExtension.max_len) catch @panic("OOM");
1840 @memcpy(extended_path_buf[0..full_path.len], full_path);
1841
18391842 while (it.next()) |ext| {
18401843 if (!supportedWindowsProgramExtension(ext)) continue;
18411844
1842 const extended_path = try mem.concat(arena, u8, &.{ full_path, ext });
1845 @memcpy(extended_path_buf[full_path.len..][0..ext.len], ext);
1846 const extended_path = extended_path_buf[0 .. full_path.len + ext.len];
18431847
18441848 if (Io.Dir.cwd().access(io, extended_path, .{ .execute = true })) |_| {
18451849 return extended_path;
......@@ -2707,4 +2711,6 @@ pub fn systemIntegrationOption(
27072711test {
27082712 _ = Cache;
27092713 _ = Step;
2714 _ = Configuration;
2715 _ = &findProgram;
27102716}
lib/std/Build/Configuration.zig+88-15
......@@ -1,10 +1,12 @@
11const Configuration = @This();
22
33const std = @import("../std.zig");
4const builtin = @import("builtin");
45const Io = std.Io;
56const Allocator = std.mem.Allocator;
67const assert = std.debug.assert;
78const max_u32 = std.math.maxInt(u32);
9const native_endian = builtin.target.cpu.arch.endian();
810
911string_bytes: []u8,
1012steps: []Step,
......@@ -3434,18 +3436,46 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration {
34343436 return result;
34353437}
34363438
3439/// Loads bits using native endianness when `value` spans multiple bytes.
3440/// On big endian architectures, `bit_offset` uses MSb 0 bit numbering.
3441/// On little endian architectures, `bit_offset` uses LSb 0 bit numbering.
3442/// See `storeBits`.
34373443pub fn loadBits(comptime Int: type, buffer: []const Int, bit_offset: usize, comptime Result: type) Result {
34383444 const index = bit_offset / @bitSizeOf(Int);
34393445 const small_bit_offset = bit_offset % @bitSizeOf(Int);
34403446 const ResultInt = @Int(.unsigned, @bitSizeOf(Result));
3441 const result: ResultInt = @truncate(buffer[index] >> @intCast(small_bit_offset));
3442 const available_bits = @bitSizeOf(Int) - small_bit_offset;
3443 if (available_bits >= @bitSizeOf(ResultInt)) return @bitCast(result);
3444 const missing_bits = @bitSizeOf(ResultInt) - available_bits;
3445 const upper: ResultInt = @truncate(buffer[index + 1] & ((@as(usize, 1) << @intCast(missing_bits)) - 1));
3446 return @bitCast(result | (upper << @intCast(available_bits)));
3447 switch (native_endian) {
3448 .little => {
3449 const result: ResultInt = @truncate(buffer[index] >> @intCast(small_bit_offset));
3450 const available_bits = @bitSizeOf(Int) - small_bit_offset;
3451 if (available_bits >= @bitSizeOf(ResultInt)) return @bitCast(result);
3452 const missing_bits = @bitSizeOf(ResultInt) - available_bits;
3453 const upper: ResultInt = @truncate(buffer[index + 1] & ((@as(usize, 1) << @intCast(missing_bits)) - 1));
3454 return @bitCast(result | (upper << @intCast(available_bits)));
3455 },
3456 .big => {
3457 const available_bits = @bitSizeOf(Int) - small_bit_offset;
3458 if (available_bits >= @bitSizeOf(ResultInt)) {
3459 const shift = available_bits - @bitSizeOf(ResultInt);
3460 const result: ResultInt = @truncate(buffer[index] >> @intCast(shift));
3461 return @bitCast(result);
3462 }
3463 const mask = (@as(Int, 1) << @intCast(available_bits)) - 1;
3464 const result: ResultInt = @intCast(buffer[index] & mask);
3465 const missing_bits = @bitSizeOf(ResultInt) - available_bits;
3466 const lower: ResultInt = @truncate(buffer[index + 1] >> @intCast(@bitSizeOf(Int) - missing_bits));
3467 return @bitCast((result << @intCast(missing_bits)) | lower);
3468 },
3469 }
34473470}
34483471
3472/// Store bits using native endianness when `value` spans multiple bytes.
3473/// On big endian architectures:
3474/// - For a given value, the bits of an earlier byte are more significant than the bits of subsequent bytes.
3475/// - `bit_offset` uses MSb 0 bit numbering.
3476/// On little endian architectures:
3477/// - For a given value, the bits of an earlier byte are less significant than the bits of subsequent bytes.
3478/// - `bit_offset` uses LSb 0 bit numbering.
34493479pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void {
34503480 const Value = @TypeOf(value);
34513481 const ValueInt = @Int(.unsigned, @bitSizeOf(Value));
......@@ -3454,27 +3484,70 @@ pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: an
34543484 const small_bit_offset = bit_offset % @bitSizeOf(Int);
34553485 const available_bits = @bitSizeOf(Int) - small_bit_offset;
34563486 if (available_bits >= @bitSizeOf(ValueInt)) {
3457 buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset));
3458 buffer[index] |= @as(Int, value_int) << @intCast(small_bit_offset);
3487 const shift = switch (native_endian) {
3488 .little => small_bit_offset,
3489 .big => available_bits - @bitSizeOf(ValueInt),
3490 };
3491 buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(shift));
3492 buffer[index] |= @as(Int, value_int) << @intCast(shift);
34593493 } else {
34603494 const DoubleInt = @Int(.unsigned, @bitSizeOf(Int) * 2);
3495 const shift = switch (native_endian) {
3496 .little => small_bit_offset,
3497 .big => @bitSizeOf(DoubleInt) - small_bit_offset - @bitSizeOf(ValueInt),
3498 };
34613499 const ptr: *align(@alignOf(Int)) DoubleInt = @ptrCast(buffer[index..][0..2]);
3462 ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset));
3463 ptr.* |= @as(DoubleInt, value_int) << @intCast(small_bit_offset);
3500 ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(shift));
3501 ptr.* |= @as(DoubleInt, value_int) << @intCast(shift);
34643502 }
34653503}
34663504
34673505test "loadBits and storeBits" {
3468 var buffer: [2]u32 = .{
3469 0b01111111000000001111111100000000,
3470 0b11111111000000001111111100000100,
3506 var buffer: [2]u32 = switch (native_endian) {
3507 .little => .{
3508 //──┐ 0b100011 (end) ┌─┐ 0b100
3509 0b01111111000000001111111100000000,
3510 // n <── bit offset 0 ┘
3511 // ┌── 0b100011 (start)
3512 0b11111111000000001111111100000100,
3513 },
3514 .big => .{
3515 // ┌─┐ 0b100 ┌── 0b100011 (start)
3516 0b11111110000000001111111100000100,
3517 //└ bit offset 0 ──> n
3518 //──┐ 0b100011 (end)
3519 0b01111111000000001111111100000000,
3520 },
34713521 };
3522
34723523 try std.testing.expectEqual(0b100, loadBits(u32, &buffer, 6, u3));
34733524 try std.testing.expectEqual(0b100011, loadBits(u32, &buffer, 29, u6));
34743525
3526 storeBits(u32, &buffer, 0, @as(u1, 0b0));
34753527 storeBits(u32, &buffer, 6, @as(u3, 0b010));
3476 storeBits(u32, &buffer, 29, @as(u6, 0b010010));
3528 storeBits(u32, &buffer, 29, @as(u6, 0b010110));
3529 storeBits(u32, &buffer, 40, @as(u17, 0b01110110011111110));
34773530
3531 try std.testing.expectEqual(0b0, loadBits(u32, &buffer, 0, u1));
34783532 try std.testing.expectEqual(0b010, loadBits(u32, &buffer, 6, u3));
3479 try std.testing.expectEqual(0b010010, loadBits(u32, &buffer, 29, u6));
3533 try std.testing.expectEqual(0b010110, loadBits(u32, &buffer, 29, u6));
3534 try std.testing.expectEqual(0b01110110011111110, loadBits(u32, &buffer, 40, u17));
3535
3536 // Test roundtripping of size/offset combinations
3537 inline for (1..32) |value_size| {
3538 for (0..64) |bit_offset| {
3539 if (value_size + bit_offset > @bitSizeOf(@TypeOf(buffer))) continue;
3540
3541 buffer = .{ 0, 0 };
3542
3543 const Value = @Int(.unsigned, value_size);
3544 const value: Value = @intCast((@as(u32, 1) << @intCast(@bitSizeOf(Value))) - 1);
3545 storeBits(u32, &buffer, bit_offset, value);
3546 std.testing.expectEqual(value, loadBits(u32, &buffer, bit_offset, Value)) catch |err| {
3547 std.debug.print("value size: {} bit offset: {}\n", .{ value_size, bit_offset });
3548 std.debug.print("buffer: {b:0>32} {b:0>32}\n", .{ buffer[0], buffer[1] });
3549 return err;
3550 };
3551 }
3552 }
34803553}
lib/std/process.zig+9-1
......@@ -315,7 +315,15 @@ pub fn replacePath(io: Io, dir: Io.Dir, options: ReplaceOptions) ReplaceError {
315315pub const ArgExpansion = enum { expand, no_expand };
316316
317317/// File name extensions supported natively by `CreateProcess()` on Windows.
318pub const WindowsExtension = enum { bat, cmd, com, exe };
318pub const WindowsExtension = enum {
319 bat,
320 cmd,
321 com,
322 exe,
323
324 /// Length of the longest supported extension (in ASCII characters)
325 pub const max_len = 3;
326};
319327
320328pub const SpawnError = error{
321329 /// The operating system does not support creating child processes.