From c3d20373ee232c7eabcf11a22c1c5e130f2a58cb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Jul 2019 19:35:10 -0400 Subject: std.unicode.utf8ToUtf16Le: improve performance on a simple test input: original utf8ToUtf16Le: elapsed: 111384483 ns (111 ms) new utf8ToUtf16Le: elapsed: 138570 ns (0 ms) it's 800x faster in debug mode and ~4500x faster in release-fast mode. this was slowing down installation of files on windows in build scripts. --- std/unicode.zig | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'std/unicode.zig') diff --git a/std/unicode.zig b/std/unicode.zig index 37a73d7500..6d47675ac3 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -560,18 +560,34 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 } /// Returns index of next character. If exact fit, returned index equals output slice length. -/// If ran out of room, returned index equals output slice length + 1. +/// Assumes there is enough space for the output. /// TODO support codepoints bigger than 16 bits pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { - const utf16le_as_bytes = @sliceToBytes(utf16le[0..]); - var end_index: usize = 0; - - var it = (try Utf8View.init(utf8)).iterator(); - while (it.nextCodepoint()) |codepoint| { - if (end_index == utf16le_as_bytes.len) return (end_index / 2) + 1; - // TODO surrogate pairs - mem.writeIntSliceLittle(u16, utf16le_as_bytes[end_index..], @intCast(u16, codepoint)); - end_index += 2; + var dest_i: usize = 0; + var src_i: usize = 0; + while (src_i < utf8.len) { + const byte = utf8[src_i]; + const n = @clz(u8, ~byte); + switch (n) { + 0 => { + utf16le[dest_i] = byte; + dest_i += 1; + src_i += 1; + continue; + }, + 2, 3, 4 => { + const next_src_i = src_i + n; + const codepoint = try utf8Decode(utf8[src_i..next_src_i]); + const short = @intCast(u16, codepoint); // TODO surrogate pairs + utf16le[dest_i] = switch (builtin.endian) { + .Little => short, + .Big => @byteSwap(u16, short), + }; + dest_i += 1; + src_i = next_src_i; + }, + else => return error.Utf8InvalidStartByte, + } } - return end_index / 2; + return dest_i; } -- cgit v1.2.3 From eaf545e24c009e308b9463d3c521c28621477b8f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Jul 2019 19:50:56 -0400 Subject: fix build on windows --- build.zig | 1 + src/buffer.hpp | 10 ++++++++++ src/util.hpp | 10 ++++++++++ std/os/windows.zig | 18 ++---------------- std/unicode.zig | 4 ++-- 5 files changed, 25 insertions(+), 18 deletions(-) (limited to 'std/unicode.zig') diff --git a/build.zig b/build.zig index ed9856a437..d6e365b02d 100644 --- a/build.zig +++ b/build.zig @@ -372,6 +372,7 @@ fn addLibUserlandStep(b: *Builder) void { artifact.bundle_compiler_rt = true; artifact.setTarget(builtin.arch, builtin.os, builtin.abi); artifact.linkSystemLibrary("c"); + artifact.linkSystemLibrary("ntdll"); const libuserland_step = b.step("libuserland", "Build the userland compiler library for use in stage1"); libuserland_step.dependOn(&artifact.step); diff --git a/src/buffer.hpp b/src/buffer.hpp index d4a911fc21..d7254c18a7 100644 --- a/src/buffer.hpp +++ b/src/buffer.hpp @@ -136,11 +136,21 @@ static inline bool buf_eql_mem(Buf *buf, const char *mem, size_t mem_len) { return mem_eql_mem(buf_ptr(buf), buf_len(buf), mem, mem_len); } +static inline bool buf_eql_mem_ignore_case(Buf *buf, const char *mem, size_t mem_len) { + assert(buf->list.length); + return mem_eql_mem_ignore_case(buf_ptr(buf), buf_len(buf), mem, mem_len); +} + static inline bool buf_eql_str(Buf *buf, const char *str) { assert(buf->list.length); return buf_eql_mem(buf, str, strlen(str)); } +static inline bool buf_eql_str_ignore_case(Buf *buf, const char *str) { + assert(buf->list.length); + return buf_eql_mem_ignore_case(buf, str, strlen(str)); +} + static inline bool buf_starts_with_mem(Buf *buf, const char *mem, size_t mem_len) { if (buf_len(buf) < mem_len) { return false; diff --git a/src/util.hpp b/src/util.hpp index 6f26725135..1fa33b30f9 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #if defined(_MSC_VER) @@ -161,6 +162,15 @@ static inline bool mem_eql_mem(const char *a_ptr, size_t a_len, const char *b_pt return false; return memcmp(a_ptr, b_ptr, a_len) == 0; } +static inline bool mem_eql_mem_ignore_case(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) { + if (a_len != b_len) + return false; + for (size_t i = 0; i < a_len; i += 1) { + if (tolower(a_ptr[i]) != tolower(b_ptr[i])) + return false; + } + return true; +} static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) { return mem_eql_mem(mem, mem_len, str, strlen(str)); diff --git a/std/os/windows.zig b/std/os/windows.zig index aa97671298..ac76e8f58f 100644 --- a/std/os/windows.zig +++ b/std/os/windows.zig @@ -267,7 +267,7 @@ pub fn GetQueuedCompletionStatus( } pub fn CloseHandle(hObject: HANDLE) void { - assert(ntdll.NtClose(hObject) == STATUS.SUCCESS); + assert(kernel32.CloseHandle(hObject) != 0); } pub fn FindClose(hFindFile: HANDLE) void { @@ -820,24 +820,9 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { return sliceToPrefixedSuffixedFileW(s, [_]u16{0}); } -/// TODO once https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761 are both solved, -/// this can be removed. Callsites that do not have performance bottlenecks -/// in this function should call `sliceToPrefixedFileW` to be future-proof. -pub fn sliceToPrefixedFileW_elidecopy(s: []const u8, result: *[PATH_MAX_WIDE + 1]u16) !void { - return sliceToPrefixedSuffixedFileW_elidecopy(s, [_]u16{0}, result); -} - pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { // TODO https://github.com/ziglang/zig/issues/2765 var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; - try sliceToPrefixedSuffixedFileW_elidecopy(s, suffix, &result); - return result; -} - -/// TODO once https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761 are both solved, -/// this can be removed. Callsites that do not have performance bottlenecks -/// in this function should call `sliceToPrefixedSuffixedFileW` to be future-proof. -pub fn sliceToPrefixedSuffixedFileW_elidecopy(s: []const u8, comptime suffix: []const u16, result: *[PATH_MAX_WIDE + suffix.len]u16) !void { // > File I/O functions in the Windows API convert "/" to "\" as part of // > converting the name to an NT-style name, except when using the "\\?\" // > prefix as detailed in the following sections. @@ -859,6 +844,7 @@ pub fn sliceToPrefixedSuffixedFileW_elidecopy(s: []const u8, comptime suffix: [] assert(end_index <= result.len); if (end_index + suffix.len > result.len) return error.NameTooLong; mem.copy(u16, result[end_index..], suffix); + return result; } inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID { diff --git a/std/unicode.zig b/std/unicode.zig index 6d47675ac3..2e96147166 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -577,7 +577,7 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { }, 2, 3, 4 => { const next_src_i = src_i + n; - const codepoint = try utf8Decode(utf8[src_i..next_src_i]); + const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; const short = @intCast(u16, codepoint); // TODO surrogate pairs utf16le[dest_i] = switch (builtin.endian) { .Little => short, @@ -586,7 +586,7 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { dest_i += 1; src_i = next_src_i; }, - else => return error.Utf8InvalidStartByte, + else => return error.InvalidUtf8, } } return dest_i; -- cgit v1.2.3