authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2024-07-15 12:03:25+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-15 12:03:25+03:00
log5675553aedf72c3cf6135025fe216fb92f09f3fa
tree89ac98703b7d5266dfcfa7f6895cb3631bb72186
parentc50f3003874d561aa696feb71f2089562307cdca
parentd48251d0f0d2de2f618afe65db3ba425b332d5c4
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20622 from squeek502/windows-arg-iterator-cleanup

`ArgIteratorWindows`: Cleanup and some optimizations

2 files changed, 62 insertions(+), 57 deletions(-)

lib/std/os/windows/kernel32.zig-3
......@@ -161,9 +161,6 @@ pub extern "kernel32" fn FormatMessageW(dwFlags: DWORD, lpSource: ?LPVOID, dwMes
161161
162162pub extern "kernel32" fn FreeEnvironmentStringsW(penv: [*:0]u16) callconv(WINAPI) BOOL;
163163
164pub extern "kernel32" fn GetCommandLineA() callconv(WINAPI) LPSTR;
165pub extern "kernel32" fn GetCommandLineW() callconv(WINAPI) LPWSTR;
166
167164pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(WINAPI) BOOL;
168165pub extern "kernel32" fn SetConsoleMode(in_hConsoleHandle: HANDLE, in_dwMode: DWORD) callconv(WINAPI) BOOL;
169166
lib/std/process.zig+62-54
......@@ -664,7 +664,7 @@ pub const ArgIteratorWasi = struct {
664664pub const ArgIteratorWindows = struct {
665665 allocator: Allocator,
666666 /// Encoded as WTF-16 LE.
667 cmd_line: [:0]const u16,
667 cmd_line: []const u16,
668668 index: usize = 0,
669669 /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices
670670 /// of each argument encoded as WTF-8.
......@@ -678,21 +678,22 @@ pub const ArgIteratorWindows = struct {
678678 ///
679679 /// The iterator stores and uses `cmd_line_w`, so its memory must be valid for
680680 /// at least as long as the returned ArgIteratorWindows.
681 pub fn init(allocator: Allocator, cmd_line_w: [*:0]const u16) InitError!ArgIteratorWindows {
682 const cmd_line = mem.sliceTo(cmd_line_w, 0);
683 const wtf8_len = unicode.calcWtf8Len(cmd_line);
681 pub fn init(allocator: Allocator, cmd_line_w: []const u16) InitError!ArgIteratorWindows {
682 const wtf8_len = unicode.calcWtf8Len(cmd_line_w);
684683
685684 // This buffer must be large enough to contain contiguous NUL-terminated slices
686 // of each argument. For arguments past the first one, space for the NUL-terminator
687 // is guaranteed due to the necessary whitespace between arugments. However, we need
688 // one extra byte to guarantee enough room for the NUL terminator if the command line
689 // ends up being exactly 1 argument long with no quotes, etc.
685 // of each argument.
686 // - During parsing, the length of a parsed argument will always be equal to
687 // to less than its unparsed length
688 // - The first argument needs one extra byte of space allocated for its NUL
689 // terminator, but for each subsequent argument the necessary whitespace
690 // between arguments guarantees room for their NUL terminator(s).
690691 const buffer = try allocator.alloc(u8, wtf8_len + 1);
691692 errdefer allocator.free(buffer);
692693
693694 return .{
694695 .allocator = allocator,
695 .cmd_line = cmd_line,
696 .cmd_line = cmd_line_w,
696697 .buffer = buffer,
697698 };
698699 }
......@@ -715,14 +716,20 @@ pub const ArgIteratorWindows = struct {
715716
716717 const eof = null;
717718
718 fn emitBackslashes(self: *ArgIteratorWindows, count: usize) void {
719 for (0..count) |_| emitCharacter(self, '\\');
719 /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`.
720 fn emitBackslashes(self: *ArgIteratorWindows, count: usize, last_emitted_code_unit: ?u16) ?u16 {
721 for (0..count) |_| {
722 self.buffer[self.end] = '\\';
723 self.end += 1;
724 }
725 return if (count != 0) '\\' else last_emitted_code_unit;
720726 }
721727
722 fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16) void {
723 const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable;
724 self.end += wtf8_len;
725
728 /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then
729 /// the previously emitted high surrogate is overwritten by the codepoint encoded
730 /// by the surrogate pair, and `null` is returned.
731 /// Otherwise, `code_unit` is emitted and returned.
732 fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 {
726733 // Because we are emitting WTF-8, we need to
727734 // check to see if we've emitted two consecutive surrogate
728735 // codepoints that form a valid surrogate pair in order
......@@ -743,28 +750,24 @@ pub const ArgIteratorWindows = struct {
743750 // and emit the codepoint it encodes, which in this
744751 // example is U+10437 (𐐷), which is encoded in UTF-8 as:
745752 // <0xF0><0x90><0x90><0xB7>
746 concatSurrogatePair(self);
747 }
748
749 fn concatSurrogatePair(self: *ArgIteratorWindows) void {
750 // Surrogate codepoints are always encoded as 3 bytes, so there
751 // must be 6 bytes for a surrogate pair to exist.
752 if (self.end - self.start >= 6) {
753 const window = self.buffer[self.end - 6 .. self.end];
754 const view = unicode.Wtf8View.init(window) catch return;
755 var it = view.iterator();
756 var pair: [2]u16 = undefined;
757 pair[0] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return);
758 if (!unicode.utf16IsHighSurrogate(std.mem.littleToNative(u16, pair[0]))) return;
759 pair[1] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return);
760 if (!unicode.utf16IsLowSurrogate(std.mem.littleToNative(u16, pair[1]))) return;
761 // We know we have a valid surrogate pair, so convert
762 // it to UTF-8, overwriting the surrogate pair's bytes
763 // and then chop off the extra bytes.
764 const len = unicode.utf16LeToUtf8(window, &pair) catch unreachable;
765 const delta = 6 - len;
766 self.end -= delta;
753 if (last_emitted_code_unit != null and
754 std.unicode.utf16IsLowSurrogate(code_unit) and
755 std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?))
756 {
757 const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable;
758
759 // Unpaired surrogate is 3 bytes long
760 const dest = self.buffer[self.end - 3 ..];
761 const len = unicode.utf8Encode(codepoint, dest) catch unreachable;
762 // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes
763 assert(len == 4);
764 self.end += 1;
765 return null;
767766 }
767
768 const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable;
769 self.end += wtf8_len;
770 return code_unit;
768771 }
769772
770773 fn yieldArg(self: *ArgIteratorWindows) [:0]const u8 {
......@@ -781,9 +784,13 @@ pub const ArgIteratorWindows = struct {
781784
782785 const eof = false;
783786
784 fn emitBackslashes(_: *ArgIteratorWindows, _: usize) void {}
787 fn emitBackslashes(_: *ArgIteratorWindows, _: usize, last_emitted_code_unit: ?u16) ?u16 {
788 return last_emitted_code_unit;
789 }
785790
786 fn emitCharacter(_: *ArgIteratorWindows, _: u16) void {}
791 fn emitCharacter(_: *ArgIteratorWindows, _: u16, last_emitted_code_unit: ?u16) ?u16 {
792 return last_emitted_code_unit;
793 }
787794
788795 fn yieldArg(_: *ArgIteratorWindows) bool {
789796 return true;
......@@ -791,6 +798,7 @@ pub const ArgIteratorWindows = struct {
791798 };
792799
793800 fn nextWithStrategy(self: *ArgIteratorWindows, comptime strategy: type) strategy.T {
801 var last_emitted_code_unit: ?u16 = null;
794802 // The first argument (the executable name) uses different parsing rules.
795803 if (self.index == 0) {
796804 if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) {
......@@ -813,15 +821,15 @@ pub const ArgIteratorWindows = struct {
813821 inside_quotes = !inside_quotes;
814822 },
815823 ' ', '\t' => {
816 if (inside_quotes)
817 strategy.emitCharacter(self, char)
818 else {
824 if (inside_quotes) {
825 last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
826 } else {
819827 self.index += 1;
820828 return strategy.yieldArg(self);
821829 }
822830 },
823831 else => {
824 strategy.emitCharacter(self, char);
832 last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
825833 },
826834 }
827835 }
......@@ -859,29 +867,28 @@ pub const ArgIteratorWindows = struct {
859867 0;
860868 switch (char) {
861869 0 => {
862 strategy.emitBackslashes(self, backslash_count);
870 last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
863871 return strategy.yieldArg(self);
864872 },
865873 ' ', '\t' => {
866 strategy.emitBackslashes(self, backslash_count);
874 last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
867875 backslash_count = 0;
868 if (inside_quotes)
869 strategy.emitCharacter(self, char)
870 else
871 return strategy.yieldArg(self);
876 if (inside_quotes) {
877 last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
878 } else return strategy.yieldArg(self);
872879 },
873880 '"' => {
874881 const char_is_escaped_quote = backslash_count % 2 != 0;
875 strategy.emitBackslashes(self, backslash_count / 2);
882 last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit);
876883 backslash_count = 0;
877884 if (char_is_escaped_quote) {
878 strategy.emitCharacter(self, '"');
885 last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit);
879886 } else {
880887 if (inside_quotes and
881888 self.index + 1 != self.cmd_line.len and
882889 mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"')
883890 {
884 strategy.emitCharacter(self, '"');
891 last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit);
885892 self.index += 1;
886893 } else {
887894 inside_quotes = !inside_quotes;
......@@ -892,9 +899,9 @@ pub const ArgIteratorWindows = struct {
892899 backslash_count += 1;
893900 },
894901 else => {
895 strategy.emitBackslashes(self, backslash_count);
902 last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
896903 backslash_count = 0;
897 strategy.emitCharacter(self, char);
904 last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
898905 },
899906 }
900907 }
......@@ -1142,7 +1149,8 @@ pub const ArgIterator = struct {
11421149 return ArgIterator{ .inner = try InnerType.init(allocator) };
11431150 }
11441151 if (native_os == .windows) {
1145 const cmd_line_w = windows.kernel32.GetCommandLineW();
1152 const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
1153 const cmd_line_w = cmd_line.Buffer.?[0 .. cmd_line.Length / 2];
11461154 return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) };
11471155 }
11481156