authorgravatar for rohlemF@gmail.comRohlem <rohlemF@gmail.com> 2020-12-23 22:10:28+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-11 17:48:19-07:00
logc96272f6186a5e843c0586513e87ffd771c5b00c
tree25da58e774494f28c44463921ba2f2387d8e3ae3
parentf301a8467c0171ab603036b5b31be48fbb40e8ad

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.

2 files changed, 67 insertions(+), 27 deletions(-)

lib/std/mem.zig+43
......@@ -2436,3 +2436,46 @@ test "freeing empty string with null-terminated sentinel" {
24362436 const empty_string = try dupeZ(testing.allocator, u8, "");
24372437 testing.allocator.free(empty_string);
24382438}
2439
2440/// Returns a slice with the given new alignment,
2441/// all other pointer attributes copied from `AttributeSource`.
2442fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) type {
2443 const info = @typeInfo(AttributeSource).Pointer;
2444 return @Type(.{
2445 .Pointer = .{
2446 .size = .Slice,
2447 .is_const = info.is_const,
2448 .is_volatile = info.is_volatile,
2449 .is_allowzero = info.is_allowzero,
2450 .alignment = new_alignment,
2451 .child = info.child,
2452 .sentinel = null,
2453 },
2454 });
2455}
2456
2457/// Returns the largest slice in the given bytes that conforms to the new alignment,
2458/// or `null` if the given bytes contain no conforming address.
2459pub fn alignInBytes(bytes: []u8, comptime new_alignment: usize) ?[]align(new_alignment) u8 {
2460 const begin_address = @ptrToInt(bytes.ptr);
2461 const end_address = begin_address + bytes.len;
2462
2463 const begin_address_aligned = mem.alignForward(begin_address, new_alignment);
2464 const new_length = std.math.sub(usize, end_address, begin_address_aligned) catch |e| switch (e) {
2465 error.Overflow => return null,
2466 };
2467 const alignment_offset = begin_address_aligned - begin_address;
2468 return @alignCast(new_alignment, bytes[alignment_offset .. alignment_offset + new_length]);
2469}
2470
2471/// Returns the largest sub-slice within the given slice that conforms to the new alignment,
2472/// or `null` if the given slice contains no conforming address.
2473pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice(@TypeOf(slice), new_alignment) {
2474 const bytes = sliceAsBytes(slice);
2475 const aligned_bytes = alignInBytes(bytes, new_alignment) orelse return null;
2476
2477 const Element = @TypeOf(slice[0]);
2478 const slice_length_bytes = aligned_bytes.len - (aligned_bytes.len % @sizeOf(Element));
2479 const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]);
2480 return @alignCast(new_alignment, aligned_slice);
2481}
lib/std/os/windows.zig+24-27
......@@ -957,29 +957,29 @@ pub fn QueryObjectName(
957957 handle: HANDLE,
958958 out_buffer: []u16,
959959) ![]u16 {
960 var full_buffer: [@sizeOf(OBJECT_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(OBJECT_NAME_INFORMATION)) = undefined;
961 var info = @ptrCast(*OBJECT_NAME_INFORMATION, &full_buffer);
960 const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
961
962 const info = @ptrCast(*OBJECT_NAME_INFORMATION, out_buffer_aligned);
962963 //buffer size is specified in bytes
964 const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) catch |e| switch (e) {
965 error.Overflow => std.math.maxInt(ULONG),
966 };
963967 //last argument would return the length required for full_buffer, not exposed here
964 const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, &full_buffer, full_buffer.len, null);
968 const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null);
965969 switch (rc) {
966970 .SUCCESS => {
967971 // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
968972 // if the object was "unnamed", not sure if this can happen for file handles
969973 if (info.Name.MaximumLength == 0) return error.Unexpected;
970 //resulting string length is specified in bytes
974 // resulting string length is specified in bytes
971975 const path_length_unterminated = @divExact(info.Name.Length, 2);
972 if (out_buffer.len < path_length_unterminated) {
973 return error.NameTooLong;
974 }
975 mem.copy(WCHAR, out_buffer[0..path_length_unterminated], info.Name.Buffer[0..path_length_unterminated]);
976 return out_buffer[0..path_length_unterminated];
976 return info.Name.Buffer[0..path_length_unterminated];
977977 },
978978 .ACCESS_DENIED => return error.AccessDenied,
979979 .INVALID_HANDLE => return error.InvalidHandle,
980 .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong,
981 //name_buffer.len >= @sizeOf(OBJECT_NAME_INFORMATION) holds statically
982 .INFO_LENGTH_MISMATCH => unreachable,
980 // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
981 // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
982 .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong,
983983 else => |e| return unexpectedStatus(e),
984984 }
985985}
......@@ -994,10 +994,11 @@ test "QueryObjectName" {
994994 var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
995995
996996 var result_path = try QueryObjectName(handle, &out_buffer);
997 const required_len_in_u16 = result_path.len + @divExact(@ptrToInt(result_path.ptr) - @ptrToInt(&out_buffer), 2) + 1;
997998 //insufficient size
998 std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. result_path.len - 1]));
999 std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
9991000 //exactly-sufficient size
1000 _ = try QueryObjectName(handle, out_buffer[0..result_path.len]);
1001 _ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
10011002}
10021003
10031004pub const GetFinalPathNameByHandleError = error{
......@@ -1029,8 +1030,7 @@ pub fn GetFinalPathNameByHandle(
10291030 fmt: GetFinalPathNameByHandleFormat,
10301031 out_buffer: []u16,
10311032) GetFinalPathNameByHandleError![]u16 {
1032 var path_buffer: [math.max(@sizeOf(FILE_NAME_INFORMATION), @sizeOf(OBJECT_NAME_INFORMATION)) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
1033 const final_path = QueryObjectName(hFile, mem.bytesAsSlice(u16, &path_buffer)) catch |err| switch (err) {
1033 const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
10341034 // we assume InvalidHandle is close enough to FileNotFound in semantics
10351035 // to not further complicate the error set
10361036 error.InvalidHandle => return error.FileNotFound,
......@@ -1040,11 +1040,7 @@ pub fn GetFinalPathNameByHandle(
10401040 switch (fmt.volume_name) {
10411041 .Nt => {
10421042 // the returned path is already in .Nt format
1043 if (out_buffer.len < final_path.len) {
1044 return error.NameTooLong;
1045 }
1046 mem.copy(u16, out_buffer, final_path);
1047 return out_buffer[0..final_path.len];
1043 return final_path;
10481044 },
10491045 .Dos => {
10501046 // parse the string to separate volume path from file path
......@@ -1153,16 +1149,17 @@ test "GetFinalPathNameByHandle" {
11531149 var buffer: [PATH_MAX_WIDE]u16 = undefined;
11541150
11551151 //check with sufficient size
1156 const nt_length = (try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer)).len;
1157 const dos_length = (try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer)).len;
1152 const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
1153 _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
11581154
1155 const required_len_in_u16 = nt_path.len + @divExact(@ptrToInt(nt_path.ptr) - @ptrToInt(&buffer), 2) + 1;
11591156 //check with insufficient size
1160 std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. nt_length - 1]));
1161 std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. dos_length - 1]));
1157 std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
1158 std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
11621159
11631160 //check with exactly-sufficient size
1164 _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..nt_length]);
1165 _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..dos_length]);
1161 _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
1162 _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
11661163}
11671164
11681165pub const QueryInformationFileError = error{Unexpected};