Skip to content

Commit befaeb3

Browse files
committed
std.os.windows.GetFinalPathNameByHandle: remove intermediate buffers
... and mem.copy operations. Requires slightly larger input buffers than result length. Add helper functions std.mem.alignInBytes and std.mem.alignInSlice.
1 parent 796f312 commit befaeb3

File tree

2 files changed

+67
-27
lines changed

2 files changed

+67
-27
lines changed

lib/std/mem.zig

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,3 +2402,46 @@ test "freeing empty string with null-terminated sentinel" {
24022402
const empty_string = try dupeZ(testing.allocator, u8, "");
24032403
testing.allocator.free(empty_string);
24042404
}
2405+
2406+
/// Returns a slice with the given new alignment,
2407+
/// all other pointer attributes copied from `AttributeSource`.
2408+
fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) type {
2409+
const info = @typeInfo(AttributeSource).Pointer;
2410+
return @Type(.{
2411+
.Pointer = .{
2412+
.size = .Slice,
2413+
.is_const = info.is_const,
2414+
.is_volatile = info.is_volatile,
2415+
.is_allowzero = info.is_allowzero,
2416+
.alignment = new_alignment,
2417+
.child = info.child,
2418+
.sentinel = null,
2419+
},
2420+
});
2421+
}
2422+
2423+
/// Returns the largest slice in the given bytes that conforms to the new alignment,
2424+
/// or `null` if the given bytes contain no conforming address.
2425+
pub fn alignInBytes(bytes: []u8, comptime new_alignment: usize) ?[]align(new_alignment) u8 {
2426+
const begin_address = @ptrToInt(bytes.ptr);
2427+
const end_address = begin_address + bytes.len;
2428+
2429+
const begin_address_aligned = mem.alignForward(begin_address, new_alignment);
2430+
const new_length = std.math.sub(usize, end_address, begin_address_aligned) catch |e| switch (e) {
2431+
error.Overflow => return null,
2432+
};
2433+
const alignment_offset = begin_address_aligned - begin_address;
2434+
return @alignCast(new_alignment, bytes[alignment_offset .. alignment_offset + new_length]);
2435+
}
2436+
2437+
/// Returns the largest sub-slice within the given slice that conforms to the new alignment,
2438+
/// or `null` if the given slice contains no conforming address.
2439+
pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice(@TypeOf(slice), new_alignment) {
2440+
const bytes = sliceAsBytes(slice);
2441+
const aligned_bytes = alignInBytes(bytes, new_alignment) orelse return null;
2442+
2443+
const Element = @TypeOf(slice[0]);
2444+
const slice_length_bytes = aligned_bytes.len - (aligned_bytes.len % @sizeOf(Element));
2445+
const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]);
2446+
return @alignCast(new_alignment, aligned_slice);
2447+
}

lib/std/os/windows.zig

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -956,29 +956,29 @@ pub fn QueryObjectName(
956956
handle: HANDLE,
957957
out_buffer: []u16,
958958
) ![]u16 {
959-
var full_buffer: [@sizeOf(OBJECT_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(OBJECT_NAME_INFORMATION)) = undefined;
960-
var info = @ptrCast(*OBJECT_NAME_INFORMATION, &full_buffer);
959+
const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
960+
961+
const info = @ptrCast(*OBJECT_NAME_INFORMATION, out_buffer_aligned);
961962
//buffer size is specified in bytes
963+
const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) catch |e| switch (e) {
964+
error.Overflow => std.math.maxInt(ULONG),
965+
};
962966
//last argument would return the length required for full_buffer, not exposed here
963-
const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, &full_buffer, full_buffer.len, null);
967+
const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null);
964968
switch (rc) {
965969
.SUCCESS => {
966970
// info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
967971
// if the object was "unnamed", not sure if this can happen for file handles
968972
if (info.Name.MaximumLength == 0) return error.Unexpected;
969-
//resulting string length is specified in bytes
973+
// resulting string length is specified in bytes
970974
const path_length_unterminated = @divExact(info.Name.Length, 2);
971-
if (out_buffer.len < path_length_unterminated) {
972-
return error.NameTooLong;
973-
}
974-
mem.copy(WCHAR, out_buffer[0..path_length_unterminated], info.Name.Buffer[0..path_length_unterminated]);
975-
return out_buffer[0..path_length_unterminated];
975+
return info.Name.Buffer[0..path_length_unterminated];
976976
},
977977
.ACCESS_DENIED => return error.AccessDenied,
978978
.INVALID_HANDLE => return error.InvalidHandle,
979-
.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong,
980-
//name_buffer.len >= @sizeOf(OBJECT_NAME_INFORMATION) holds statically
981-
.INFO_LENGTH_MISMATCH => unreachable,
979+
// triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
980+
// or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
981+
.INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong,
982982
else => |e| return unexpectedStatus(e),
983983
}
984984
}
@@ -993,10 +993,11 @@ test "QueryObjectName" {
993993
var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
994994

995995
var result_path = try QueryObjectName(handle, &out_buffer);
996+
const required_len_in_u16 = result_path.len + @divExact(@ptrToInt(result_path.ptr) - @ptrToInt(&out_buffer), 2) + 1;
996997
//insufficient size
997-
std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. result_path.len - 1]));
998+
std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
998999
//exactly-sufficient size
999-
_ = try QueryObjectName(handle, out_buffer[0..result_path.len]);
1000+
_ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
10001001
}
10011002

10021003
pub const GetFinalPathNameByHandleError = error{
@@ -1028,8 +1029,7 @@ pub fn GetFinalPathNameByHandle(
10281029
fmt: GetFinalPathNameByHandleFormat,
10291030
out_buffer: []u16,
10301031
) GetFinalPathNameByHandleError![]u16 {
1031-
var path_buffer: [math.max(@sizeOf(FILE_NAME_INFORMATION), @sizeOf(OBJECT_NAME_INFORMATION)) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
1032-
const final_path = QueryObjectName(hFile, mem.bytesAsSlice(u16, &path_buffer)) catch |err| switch (err) {
1032+
const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
10331033
// we assume InvalidHandle is close enough to FileNotFound in semantics
10341034
// to not further complicate the error set
10351035
error.InvalidHandle => return error.FileNotFound,
@@ -1039,11 +1039,7 @@ pub fn GetFinalPathNameByHandle(
10391039
switch (fmt.volume_name) {
10401040
.Nt => {
10411041
// the returned path is already in .Nt format
1042-
if (out_buffer.len < final_path.len) {
1043-
return error.NameTooLong;
1044-
}
1045-
mem.copy(u16, out_buffer, final_path);
1046-
return out_buffer[0..final_path.len];
1042+
return final_path;
10471043
},
10481044
.Dos => {
10491045
// parse the string to separate volume path from file path
@@ -1152,16 +1148,17 @@ test "GetFinalPathNameByHandle" {
11521148
var buffer: [PATH_MAX_WIDE]u16 = undefined;
11531149

11541150
//check with sufficient size
1155-
const nt_length = (try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer)).len;
1156-
const dos_length = (try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer)).len;
1151+
const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
1152+
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
11571153

1154+
const required_len_in_u16 = nt_path.len + @divExact(@ptrToInt(nt_path.ptr) - @ptrToInt(&buffer), 2) + 1;
11581155
//check with insufficient size
1159-
std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. nt_length - 1]));
1160-
std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. dos_length - 1]));
1156+
std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
1157+
std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
11611158

11621159
//check with exactly-sufficient size
1163-
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..nt_length]);
1164-
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..dos_length]);
1160+
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
1161+
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
11651162
}
11661163

11671164
pub const QueryInformationFileError = error{Unexpected};

0 commit comments

Comments
 (0)