commit 4cc3e1c05ea7ed62c5961b5a014c83fd98a72021 (tree)
parent 114ea92c09b6f27fe7596fddc4b114a31bf1c334
Author: Andrew Kelley <andrew@ziglang.org>
Date: Sun, 18 Jan 2026 21:39:45 +0100
Merge pull request 'libc: use common implementations for `string.h` and `strings.h`' (#30872) from GasInfinity/zig:libc-string(s) into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30872
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
Diffstat:
45 files changed, 434 insertions(+), 1191 deletions(-)
diff --git a/lib/c.zig b/lib/c.zig
@@ -13,17 +13,20 @@ pub const panic = if (builtin.is_test)
else
std.debug.no_panic;
+// NOTE: `libzigc` aims to be a standalone libc and provide ABI compatibility with its bundled libc's.
+// Some of them like `mingw` are not fully statically linked so some symbols don't need to be exported.
+
comptime {
_ = @import("c/inttypes.zig");
_ = @import("c/ctype.zig");
_ = @import("c/stdlib.zig");
_ = @import("c/math.zig");
+ _ = @import("c/string.zig");
+ _ = @import("c/strings.zig");
_ = @import("c/wchar.zig");
if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
// Files specific to musl and wasi-libc.
- _ = @import("c/string.zig");
- _ = @import("c/strings.zig");
}
if (builtin.target.isMuslLibC()) {
diff --git a/lib/c/string.zig b/lib/c/string.zig
@@ -3,97 +3,292 @@ const std = @import("std");
const common = @import("common.zig");
comptime {
- @export(&strcmp, .{ .name = "strcmp", .linkage = common.linkage, .visibility = common.visibility });
- @export(&strncmp, .{ .name = "strncmp", .linkage = common.linkage, .visibility = common.visibility });
- @export(&strcasecmp, .{ .name = "strcasecmp", .linkage = common.linkage, .visibility = common.visibility });
- @export(&strncasecmp, .{ .name = "strncasecmp", .linkage = common.linkage, .visibility = common.visibility });
- @export(&__strcasecmp_l, .{ .name = "__strcasecmp_l", .linkage = common.linkage, .visibility = common.visibility });
- @export(&__strncasecmp_l, .{ .name = "__strncasecmp_l", .linkage = common.linkage, .visibility = common.visibility });
- @export(&__strcasecmp_l, .{ .name = "strcasecmp_l", .linkage = .weak, .visibility = common.visibility });
- @export(&__strncasecmp_l, .{ .name = "strncasecmp_l", .linkage = .weak, .visibility = common.visibility });
-}
-
-fn strcmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int {
- // We need to perform unsigned comparisons.
- return switch (std.mem.orderZ(u8, @ptrCast(s1), @ptrCast(s2))) {
- .lt => -1,
+ if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
+ // memcpy implemented in compiler_rt
+ // memmove implemented in compiler_rt
+ // memset implemented in compiler_rt
+ // memcmp implemented in compiler_rt
+ @export(&memchr, .{ .name = "memchr", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strcpy, .{ .name = "strcpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strncpy, .{ .name = "strncpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strcat, .{ .name = "strcat", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strncat, .{ .name = "strncat", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strcmp, .{ .name = "strcmp", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strncmp, .{ .name = "strncmp", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strcoll, .{ .name = "strcoll", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strxfrm, .{ .name = "strxfrm", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strchr, .{ .name = "strchr", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strrchr, .{ .name = "strrchr", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strcspn, .{ .name = "strcspn", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strspn, .{ .name = "strspn", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strpbrk, .{ .name = "strpbrk", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strstr, .{ .name = "strstr", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strtok, .{ .name = "strtok", .linkage = common.linkage, .visibility = common.visibility });
+ // strlen is in compiler_rt
+
+ @export(&strtok_r, .{ .name = "strtok_r", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&stpcpy, .{ .name = "stpcpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&stpncpy, .{ .name = "stpncpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strnlen, .{ .name = "strnlen", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&memmem, .{ .name = "memmem", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&memccpy, .{ .name = "memccpy", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&strsep, .{ .name = "strsep", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strlcat, .{ .name = "strlcat", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strlcpy, .{ .name = "strlcpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&explicit_bzero, .{ .name = "explicit_bzero", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&strchrnul, .{ .name = "strchrnul", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strcasestr, .{ .name = "strcasestr", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&memrchr, .{ .name = "memrchr", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&mempcpy, .{ .name = "mempcpy", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&__strcoll_l, .{ .name = "__strcoll_l", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&__strxfrm_l, .{ .name = "__strxfrm_l", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&__strcoll_l, .{ .name = "strcoll_l", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&__strxfrm_l, .{ .name = "strxfrm_l", .linkage = common.linkage, .visibility = common.visibility });
+
+ // These symbols are not in the public ABI of musl/wasi. However they depend on these exports internally.
+ @export(&stpcpy, .{ .name = "__stpcpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&stpncpy, .{ .name = "__stpncpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strchrnul, .{ .name = "__strchrnul", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&memrchr, .{ .name = "__memrchr", .linkage = common.linkage, .visibility = common.visibility });
+ }
+
+ if (builtin.target.isMinGW()) {
+ @export(&strnlen, .{ .name = "strnlen", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&mempcpy, .{ .name = "mempcpy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strtok_r, .{ .name = "strtok_r", .linkage = common.linkage, .visibility = common.visibility });
+ }
+}
+
+fn memchr(ptr: *const anyopaque, value: c_int, len: usize) callconv(.c) ?*anyopaque {
+ const bytes: [*]const u8 = @ptrCast(ptr);
+ return @constCast(bytes[std.mem.findScalar(u8, bytes[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]);
+}
+
+fn strcpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char) callconv(.c) [*]c_char {
+ _ = stpcpy(dst, src);
+ return dst;
+}
+
+fn strncpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) [*]c_char {
+ _ = stpncpy(dst, src, max);
+ return dst;
+}
+
+fn strcat(noalias dst: [*:0]c_char, noalias src: [*:0]const c_char) callconv(.c) [*:0]c_char {
+ return strncat(dst, src, std.math.maxInt(usize));
+}
+
+fn strncat(noalias dst: [*:0]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) [*:0]c_char {
+ const dst_len = std.mem.len(@as([*:0]u8, @ptrCast(dst)));
+ const src_len = strnlen(src, max);
+
+ @memcpy(dst[dst_len..][0..src_len], src[0..src_len]);
+ dst[dst_len + src_len] = 0;
+ return dst[0..(dst_len + src_len) :0].ptr;
+}
+
+fn strcmp(a: [*:0]const c_char, b: [*:0]const c_char) callconv(.c) c_int {
+ return strncmp(a, b, std.math.maxInt(usize));
+}
+
+fn strncmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) callconv(.c) c_int {
+ return switch (std.mem.boundedOrderZ(u8, @ptrCast(a), @ptrCast(b), max)) {
.eq => 0,
.gt => 1,
+ .lt => -1,
};
}
-fn strncmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c) c_int {
- if (n == 0) return 0;
+fn strcoll(a: [*:0]const c_char, b: [*:0]const c_char) callconv(.c) c_int {
+ return strcmp(a, b);
+}
- var l: [*:0]const u8 = @ptrCast(s1);
- var r: [*:0]const u8 = @ptrCast(s2);
- var i = n - 1;
+fn __strcoll_l(a: [*:0]const c_char, b: [*:0]const c_char, locale: *anyopaque) callconv(.c) c_int {
+ _ = locale;
+ return strcoll(a, b);
+}
- while (l[0] != 0 and r[0] != 0 and i != 0 and l[0] == r[0]) {
- l += 1;
- r += 1;
- i -= 1;
- }
+// NOTE: If 'max' is 0, 'dst' is allowed to be a null pointer
+fn strxfrm(noalias dst: ?[*]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) usize {
+ const src_len = std.mem.len(@as([*:0]const u8, @ptrCast(src)));
+ if (src_len < max) @memcpy(dst.?[0 .. src_len + 1], src[0 .. src_len + 1]);
+ return src_len;
+}
+
+fn __strxfrm_l(noalias dst: ?[*]c_char, noalias src: [*:0]const c_char, max: usize, locale: *anyopaque) callconv(.c) usize {
+ _ = locale;
+ return strxfrm(dst, src, max);
+}
+
+fn strchr(str: [*:0]const c_char, value: c_int) callconv(.c) ?[*:0]c_char {
+ const str_u8: [*:0]const u8 = @ptrCast(str);
+ const len = std.mem.len(str_u8);
- return @as(c_int, l[0]) - @as(c_int, r[0]);
+ if (value == 0) return @constCast(str + len);
+ return @constCast(str[std.mem.findScalar(u8, str_u8[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]);
}
-fn strcasecmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int {
- const toLower = std.ascii.toLower;
- var l: [*:0]const u8 = @ptrCast(s1);
- var r: [*:0]const u8 = @ptrCast(s2);
+fn strrchr(str: [*:0]const c_char, value: c_int) callconv(.c) ?[*:0]c_char {
+ const str_u8: [*:0]const u8 = @ptrCast(str);
+ // std.mem.len(str) + 1 to not special case '\0'
+ return @constCast(str[std.mem.findScalarLast(u8, str_u8[0 .. std.mem.len(str_u8) + 1], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]);
+}
+
+fn strcspn(dst: [*:0]const c_char, values: [*:0]const c_char) callconv(.c) usize {
+ const dst_slice = std.mem.span(@as([*:0]const u8, @ptrCast(dst)));
+ return std.mem.findAny(u8, dst_slice, std.mem.span(@as([*:0]const u8, @ptrCast(values)))) orelse dst_slice.len;
+}
+
+fn strspn(dst: [*:0]const c_char, values: [*:0]const c_char) callconv(.c) usize {
+ const dst_slice = std.mem.span(@as([*:0]const u8, @ptrCast(dst)));
+ return std.mem.findNone(u8, dst_slice, std.mem.span(@as([*:0]const u8, @ptrCast(values)))) orelse dst_slice.len;
+}
- while (l[0] != 0 and r[0] != 0 and (l[0] == r[0] or toLower(l[0]) == toLower(r[0]))) {
- l += 1;
- r += 1;
+fn strpbrk(haystack: [*:0]const c_char, needle: [*:0]const c_char) callconv(.c) ?[*:0]c_char {
+ return @constCast(haystack[std.mem.findAny(u8, std.mem.span(@as([*:0]const u8, @ptrCast(haystack))), std.mem.span(@as([*:0]const u8, @ptrCast(needle)))) orelse return null ..]);
+}
+
+fn strstr(haystack: [*:0]const c_char, needle: [*:0]const c_char) callconv(.c) ?[*:0]c_char {
+ return @constCast(haystack[std.mem.find(u8, std.mem.span(@as([*:0]const u8, @ptrCast(haystack))), std.mem.span(@as([*:0]const u8, @ptrCast(needle)))) orelse return null ..]);
+}
+
+fn strtok(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char) callconv(.c) ?[*:0]c_char {
+ const state = struct {
+ var str: ?[*:0]c_char = null;
+ };
+
+ return strtok_r(maybe_str, values, &state.str);
+}
+
+// strlen is in compiler_rt
+
+fn strtok_r(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char, noalias state: *?[*:0]c_char) callconv(.c) ?[*:0]c_char {
+ const str = if (maybe_str) |str|
+ str
+ else if (state.*) |state_str|
+ state_str
+ else
+ return null;
+
+ const str_bytes = std.mem.span(@as([*:0]u8, @ptrCast(str)));
+ const values_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(values)));
+ const tok_start = std.mem.findNone(u8, str_bytes, values_bytes) orelse return null;
+
+ if (std.mem.findAnyPos(u8, str_bytes, tok_start, values_bytes)) |tok_end| {
+ str[tok_end] = 0;
+ state.* = str[tok_end + 1 ..];
+ } else {
+ state.* = str[str_bytes.len..];
}
- return @as(c_int, toLower(l[0])) - @as(c_int, toLower(r[0]));
+ return str[tok_start..];
}
-fn __strcasecmp_l(s1: [*:0]const c_char, s2: [*:0]const c_char, locale: *anyopaque) callconv(.c) c_int {
- _ = locale;
- return strcasecmp(s1, s2);
+fn stpcpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char) callconv(.c) [*]c_char {
+ const src_len = std.mem.len(@as([*:0]const u8, @ptrCast(src)));
+ @memcpy(dst[0 .. src_len + 1], src[0 .. src_len + 1]);
+ return dst + src_len;
+}
+
+fn stpncpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) [*]c_char {
+ const src_len = strnlen(src, max);
+ const copying_len = @min(max, src_len);
+ @memcpy(dst[0..copying_len], src[0..copying_len]);
+ @memset(dst[copying_len..][0 .. max - copying_len], 0x00);
+ return dst + copying_len;
+}
+
+fn strnlen(str: [*:0]const c_char, max: usize) callconv(.c) usize {
+ return std.mem.findScalar(u8, @ptrCast(str[0..max]), 0) orelse max;
}
-fn strncasecmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c) c_int {
- const toLower = std.ascii.toLower;
- var l: [*:0]const u8 = @ptrCast(s1);
- var r: [*:0]const u8 = @ptrCast(s2);
- var i = n - 1;
+fn memmem(haystack: *const anyopaque, haystack_len: usize, needle: *const anyopaque, needle_len: usize) callconv(.c) ?*anyopaque {
+ const haystack_bytes: [*:0]const u8 = @ptrCast(haystack);
+ const needle_bytes: [*:0]const u8 = @ptrCast(needle);
+
+ return @constCast(haystack_bytes[std.mem.find(u8, haystack_bytes[0..haystack_len], needle_bytes[0..needle_len]) orelse return null ..]);
+}
- while (l[0] != 0 and r[0] != 0 and i != 0 and (l[0] == r[0] or toLower(l[0]) == toLower(r[0]))) {
- l += 1;
- r += 1;
- i -= 1;
+fn strsep(maybe_str: *?[*:0]c_char, values: [*:0]const c_char) callconv(.c) ?[*]c_char {
+ if (maybe_str.*) |str| {
+ const values_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(values)));
+ const str_bytes = std.mem.span(@as([*:0]u8, @ptrCast(str)));
+ const found = std.mem.findAny(u8, str_bytes, values_bytes) orelse {
+ maybe_str.* = null;
+ return str;
+ };
+
+ str[found] = 0;
+ maybe_str.* = str[found + 1 ..];
+ return str;
}
- return @as(c_int, toLower(l[0])) - @as(c_int, toLower(r[0]));
+ return null;
}
-fn __strncasecmp_l(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize, locale: *anyopaque) callconv(.c) c_int {
- _ = locale;
- return strncasecmp(s1, s2, n);
-}
-
-test strcasecmp {
- try std.testing.expect(strcasecmp(@ptrCast("a"), @ptrCast("b")) < 0);
- try std.testing.expect(strcasecmp(@ptrCast("b"), @ptrCast("a")) > 0);
- try std.testing.expect(strcasecmp(@ptrCast("A"), @ptrCast("b")) < 0);
- try std.testing.expect(strcasecmp(@ptrCast("b"), @ptrCast("A")) > 0);
- try std.testing.expect(strcasecmp(@ptrCast("A"), @ptrCast("A")) == 0);
- try std.testing.expect(strcasecmp(@ptrCast("B"), @ptrCast("b")) == 0);
- try std.testing.expect(strcasecmp(@ptrCast("bb"), @ptrCast("AA")) > 0);
-}
-
-test strncasecmp {
- try std.testing.expect(strncasecmp(@ptrCast("a"), @ptrCast("b"), 1) < 0);
- try std.testing.expect(strncasecmp(@ptrCast("b"), @ptrCast("a"), 1) > 0);
- try std.testing.expect(strncasecmp(@ptrCast("A"), @ptrCast("b"), 1) < 0);
- try std.testing.expect(strncasecmp(@ptrCast("b"), @ptrCast("A"), 1) > 0);
- try std.testing.expect(strncasecmp(@ptrCast("A"), @ptrCast("A"), 1) == 0);
- try std.testing.expect(strncasecmp(@ptrCast("B"), @ptrCast("b"), 1) == 0);
- try std.testing.expect(strncasecmp(@ptrCast("bb"), @ptrCast("AA"), 2) > 0);
+fn strlcat(dst: [*:0]c_char, src: [*:0]const c_char, dst_total_len: usize) callconv(.c) usize {
+ const dst_len = strnlen(dst, dst_total_len);
+ const src_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(src)));
+
+ if (dst_total_len == dst_len) return dst_len + src_bytes.len;
+
+ const copying_len = @min(dst_total_len - (dst_len + 1), src_bytes.len);
+
+ @memcpy(dst[dst_len..][0..copying_len], src[0..copying_len]);
+ dst[dst_len + copying_len] = 0;
+ return dst_len + src_bytes.len;
+}
+
+fn strlcpy(dst: [*]c_char, src: [*:0]const c_char, dst_total_len: usize) callconv(.c) usize {
+ const src_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(src)));
+ if (dst_total_len != 0) {
+ const copying_len = @min(src_bytes.len, dst_total_len - 1);
+ @memcpy(dst[0..copying_len], src[0..copying_len]);
+ dst[copying_len] = 0;
+ }
+ return src_bytes.len;
+}
+
+fn memccpy(noalias dst: *anyopaque, noalias src: *const anyopaque, value: c_int, len: usize) callconv(.c) *anyopaque {
+ const dst_bytes: [*]u8 = @ptrCast(dst);
+ const src_bytes: [*]const u8 = @ptrCast(src);
+ const value_u8: u8 = @truncate(@as(c_uint, @bitCast(value)));
+ const copying_len = std.mem.findScalar(u8, src_bytes[0..len], value_u8) orelse len;
+ @memcpy(dst_bytes[0..copying_len], src_bytes[0..copying_len]);
+ return dst_bytes + copying_len;
+}
+
+fn explicit_bzero(ptr: *anyopaque, len: usize) callconv(.c) void {
+ const bytes: [*]u8 = @ptrCast(ptr);
+ std.crypto.secureZero(u8, bytes[0..len]);
+}
+
+fn strchrnul(str: [*:0]const c_char, value: c_int) callconv(.c) [*:0]c_char {
+ const str_u8: [*:0]const u8 = @ptrCast(str);
+ const len = std.mem.len(str_u8);
+
+ if (value == 0) return @constCast(str + len);
+ return @constCast(str[std.mem.findScalar(u8, str_u8[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse len ..]);
+}
+
+fn strcasestr(haystack: [*:0]const c_char, needle: [*:0]const c_char) callconv(.c) ?[*:0]c_char {
+ return @constCast(haystack[std.ascii.findIgnoreCase(std.mem.span(@as([*:0]const u8, @ptrCast(haystack))), std.mem.span(@as([*:0]const u8, @ptrCast(needle)))) orelse return null ..]);
+}
+
+fn memrchr(ptr: *const anyopaque, value: c_int, len: usize) callconv(.c) ?*anyopaque {
+ const bytes: [*]const u8 = @ptrCast(ptr);
+ return @constCast(bytes[std.mem.findScalarLast(u8, bytes[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]);
+}
+
+fn mempcpy(noalias dst: *anyopaque, noalias src: *const anyopaque, len: usize) callconv(.c) *anyopaque {
+ const dst_bytes: [*]u8 = @ptrCast(dst);
+ const src_bytes: [*]const u8 = @ptrCast(src);
+ @memcpy(dst_bytes[0..len], src_bytes[0..len]);
+ return dst_bytes + len;
}
test strncmp {
diff --git a/lib/c/strings.zig b/lib/c/strings.zig
@@ -1,8 +1,34 @@
const std = @import("std");
const common = @import("common.zig");
+const builtin = @import("builtin");
comptime {
- @export(&bzero, .{ .name = "bzero", .linkage = common.linkage, .visibility = common.visibility });
+ if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
+ // bcmp is implemented in compiler_rt
+ @export(&bcopy, .{ .name = "bcopy", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&bzero, .{ .name = "bzero", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&index, .{ .name = "index", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&rindex, .{ .name = "rindex", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&ffs, .{ .name = "ffs", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&ffsl, .{ .name = "ffsl", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&ffsll, .{ .name = "ffsll", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&strcasecmp, .{ .name = "strcasecmp", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&strncasecmp, .{ .name = "strncasecmp", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&__strcasecmp_l, .{ .name = "__strcasecmp_l", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&__strncasecmp_l, .{ .name = "__strncasecmp_l", .linkage = common.linkage, .visibility = common.visibility });
+
+ @export(&__strcasecmp_l, .{ .name = "strcasecmp_l", .linkage = .weak, .visibility = common.visibility });
+ @export(&__strncasecmp_l, .{ .name = "strncasecmp_l", .linkage = .weak, .visibility = common.visibility });
+ }
+}
+
+fn bcopy(src: *const anyopaque, dst: *anyopaque, len: usize) callconv(.c) void {
+ const src_bytes: [*]const u8 = @ptrCast(src);
+ const dst_bytes: [*]u8 = @ptrCast(dst);
+ @memmove(dst_bytes[0..len], src_bytes[0..len]);
}
fn bzero(s: *anyopaque, n: usize) callconv(.c) void {
@@ -10,6 +36,52 @@ fn bzero(s: *anyopaque, n: usize) callconv(.c) void {
@memset(s_cast[0..n], 0);
}
+fn index(str: [*:0]const c_char, value: c_int) callconv(.c) ?[*:0]c_char {
+ return @constCast(str[std.mem.findScalar(u8, std.mem.span(@as([*:0]const u8, @ptrCast(str))), @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]);
+}
+
+fn rindex(str: [*:0]const c_char, value: c_int) callconv(.c) ?[*:0]c_char {
+ return @constCast(str[std.mem.findScalarLast(u8, std.mem.span(@as([*:0]const u8, @ptrCast(str))), @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]);
+}
+
+fn firstBitSet(comptime T: type, value: T) T {
+ return @bitSizeOf(T) - @clz(value);
+}
+
+fn ffs(i: c_int) callconv(.c) c_int {
+ return firstBitSet(c_int, i);
+}
+
+fn ffsl(i: c_long) callconv(.c) c_long {
+ return firstBitSet(c_long, i);
+}
+
+fn ffsll(i: c_longlong) callconv(.c) c_longlong {
+ return firstBitSet(c_longlong, i);
+}
+
+fn strcasecmp(a: [*:0]const c_char, b: [*:0]const c_char) callconv(.c) c_int {
+ return strncasecmp(a, b, std.math.maxInt(usize));
+}
+
+fn __strcasecmp_l(a: [*:0]const c_char, b: [*:0]const c_char, locale: *anyopaque) callconv(.c) c_int {
+ _ = locale;
+ return strcasecmp(a, b);
+}
+
+fn strncasecmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) callconv(.c) c_int {
+ return switch (std.ascii.boundedOrderIgnoreCaseZ(@ptrCast(a), @ptrCast(b), max)) {
+ .eq => 0,
+ .gt => 1,
+ .lt => -1,
+ };
+}
+
+fn __strncasecmp_l(a: [*:0]const c_char, b: [*:0]const c_char, n: usize, locale: *anyopaque) callconv(.c) c_int {
+ _ = locale;
+ return strncasecmp(a, b, n);
+}
+
test bzero {
var array: [10]u8 = [_]u8{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
var a = std.mem.zeroes([array.len]u8);
@@ -17,3 +89,33 @@ test bzero {
bzero(&array[0], 9);
try std.testing.expect(std.mem.eql(u8, &array, &a));
}
+
+test firstBitSet {
+ try std.testing.expectEqual(0, firstBitSet(usize, 0));
+
+ for (0..@bitSizeOf(usize)) |i| {
+ const bit = @as(usize, 1) << @intCast(i);
+
+ try std.testing.expectEqual(i + 1, firstBitSet(usize, bit));
+ }
+}
+
+test strcasecmp {
+ try std.testing.expect(strcasecmp(@ptrCast("a"), @ptrCast("b")) < 0);
+ try std.testing.expect(strcasecmp(@ptrCast("b"), @ptrCast("a")) > 0);
+ try std.testing.expect(strcasecmp(@ptrCast("A"), @ptrCast("b")) < 0);
+ try std.testing.expect(strcasecmp(@ptrCast("b"), @ptrCast("A")) > 0);
+ try std.testing.expect(strcasecmp(@ptrCast("A"), @ptrCast("A")) == 0);
+ try std.testing.expect(strcasecmp(@ptrCast("B"), @ptrCast("b")) == 0);
+ try std.testing.expect(strcasecmp(@ptrCast("bb"), @ptrCast("AA")) > 0);
+}
+
+test strncasecmp {
+ try std.testing.expect(strncasecmp(@ptrCast("a"), @ptrCast("b"), 1) < 0);
+ try std.testing.expect(strncasecmp(@ptrCast("b"), @ptrCast("a"), 1) > 0);
+ try std.testing.expect(strncasecmp(@ptrCast("A"), @ptrCast("b"), 1) < 0);
+ try std.testing.expect(strncasecmp(@ptrCast("b"), @ptrCast("A"), 1) > 0);
+ try std.testing.expect(strncasecmp(@ptrCast("A"), @ptrCast("A"), 1) == 0);
+ try std.testing.expect(strncasecmp(@ptrCast("B"), @ptrCast("b"), 1) == 0);
+ try std.testing.expect(strncasecmp(@ptrCast("bb"), @ptrCast("AA"), 2) > 0);
+}
diff --git a/lib/c/wchar.zig b/lib/c/wchar.zig
@@ -26,6 +26,7 @@ comptime {
@export(&wcsspn, .{ .name = "wcsspn", .linkage = common.linkage, .visibility = common.visibility });
@export(&wcscspn, .{ .name = "wcscspn", .linkage = common.linkage, .visibility = common.visibility });
@export(&wcspbrk, .{ .name = "wcspbrk", .linkage = common.linkage, .visibility = common.visibility });
+ @export(&wcstok, .{ .name = "wcstok", .linkage = common.linkage, .visibility = common.visibility });
@export(&wcsstr, .{ .name = "wcsstr", .linkage = common.linkage, .visibility = common.visibility });
@export(&wcswcs, .{ .name = "wcswcs", .linkage = common.linkage, .visibility = common.visibility });
}
@@ -46,10 +47,8 @@ fn wmemchr(ptr: [*]const wchar_t, value: wchar_t, len: usize) callconv(.c) ?[*]w
}
fn wmemcmp(a: [*]const wchar_t, b: [*]const wchar_t, len: usize) callconv(.c) c_int {
- const idx = std.mem.findDiff(wchar_t, a[0..len], b[0..len]) orelse return 0;
-
- return switch (std.math.order(a[idx], b[idx])) {
- .eq => unreachable,
+ return switch (std.mem.order(wchar_t, a[0..len], b[0..len])) {
+ .eq => 0,
.gt => 1,
.lt => -1,
};
@@ -88,40 +87,24 @@ fn wcscmp(a: [*:0]const wchar_t, b: [*:0]const wchar_t) callconv(.c) c_int {
}
fn wcsncmp(a: [*:0]const wchar_t, b: [*:0]const wchar_t, max: usize) callconv(.c) c_int {
- const a_slice = a[0..wcsnlen(a, max)];
- const b_slice = b[0..wcsnlen(b, max)];
-
- return switch (std.math.order(a_slice.len, b_slice.len)) {
- .eq => blk: {
- const idx = std.mem.findDiff(wchar_t, a_slice, b_slice) orelse break :blk 0;
-
- break :blk switch (std.math.order(a[idx], b[idx])) {
- .eq => unreachable,
- .gt => 1,
- .lt => -1,
- };
- },
+ return switch (std.mem.boundedOrderZ(wchar_t, a, b, max)) {
+ .eq => 0,
.gt => 1,
.lt => -1,
};
}
fn wcpcpy(noalias dst: [*]wchar_t, noalias src: [*:0]const wchar_t) callconv(.c) [*]wchar_t {
- const src_slice = std.mem.span(src);
- @memcpy(dst[0..src_slice.len], src_slice);
- dst[src_slice.len] = 0;
- return dst + src_slice.len;
- // XXX: LLVM bug?
- // return wcpncpy(dst, src, std.math.maxInt(usize));
+ const src_len = std.mem.len(src);
+ @memcpy(dst[0 .. src_len + 1], src[0 .. src_len + 1]);
+ return dst + src_len;
}
fn wcpncpy(noalias dst: [*]wchar_t, noalias src: [*:0]const wchar_t, max: usize) callconv(.c) [*]wchar_t {
const src_len = wcsnlen(src, max);
const copying_len = @min(max, src_len);
-
@memcpy(dst[0..copying_len], src[0..copying_len]);
-
- if (copying_len < max) dst[copying_len] = 0;
+ @memset(dst[copying_len..][0 .. max - copying_len], 0x00);
return dst + copying_len;
}
@@ -175,6 +158,28 @@ fn wcspbrk(haystack: [*:0]const wchar_t, needle: [*:0]const wchar_t) callconv(.c
return @constCast(haystack[std.mem.findAny(wchar_t, std.mem.span(haystack), std.mem.span(needle)) orelse return null ..]);
}
+fn wcstok(noalias maybe_str: ?[*:0]wchar_t, noalias values: [*:0]const wchar_t, noalias state: *?[*:0]wchar_t) callconv(.c) ?[*:0]wchar_t {
+ const str = if (maybe_str) |str|
+ str
+ else if (state.*) |state_str|
+ state_str
+ else
+ return null;
+
+ const str_chars = std.mem.span(str);
+ const values_chars = std.mem.span(values);
+ const tok_start = std.mem.findNone(wchar_t, str_chars, values_chars) orelse return null;
+
+ if (std.mem.findAnyPos(wchar_t, str_chars, tok_start, values_chars)) |tok_end| {
+ str[tok_end] = 0;
+ state.* = str[tok_end + 1 ..];
+ } else {
+ state.* = str[str_chars.len..];
+ }
+
+ return str[tok_start..];
+}
+
fn wcsstr(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar_t) callconv(.c) ?[*:0]wchar_t {
return @constCast(haystack[std.mem.find(wchar_t, std.mem.span(haystack), std.mem.span(needle)) orelse return null ..]);
}
diff --git a/lib/libc/mingw/misc/mempcpy.c b/lib/libc/mingw/misc/mempcpy.c
@@ -1,12 +0,0 @@
-#define __CRT__NO_INLINE
-#include <string.h>
-
-void * __cdecl
-mempcpy (void *d, const void *s, size_t len)
-{
- char *r = ((char *) d) + len;
- if (len != 0)
- memcpy (d, s, len);
- return r;
-}
-
diff --git a/lib/libc/mingw/misc/strnlen.c b/lib/libc/mingw/misc/strnlen.c
@@ -1,11 +0,0 @@
-#define __CRT__NO_INLINE
-#include <string.h>
-
-size_t __cdecl strnlen (const char *s, size_t maxlen)
-{
- const char *s2 = s;
- while ((size_t) (s2 - s) < maxlen && *s2)
- ++s2;
- return s2 - s;
-}
-
diff --git a/lib/libc/mingw/stdio/strtok_r.c b/lib/libc/mingw/stdio/strtok_r.c
@@ -1,98 +0,0 @@
-/*-
- * Copyright (c) 1998 Softweyr LLC. All rights reserved.
- *
- * strtok_r, from Berkeley strtok
- * Oct 13, 1998 by Wes Peters <wes@softweyr.com>
- *
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notices, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notices, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
- * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define _POSIX_SOURCE 1
-#include <string.h>
-
-/*
- * strtok_r documentation:
- * http://pubs.opengroup.org/onlinepubs/009695399/functions/strtok.html
- *
- * Implementation:
- * http://svnweb.freebsd.org/base/head/lib/libc/string/strtok.c?view=co
- *
- * strtok_r cannot completely be implemented by strtok because of the internal state.
- * It breaks when used on 2 strings where they are scanned A, B then A, B.
- * Thread-safety is not the issue.
- *
- * Sample strtok implemenatation, note the internal state:
- * char *strtok(char *s, const char *d) { static char *t; return strtok_r(s,d,t); }
- *
- */
-
-char *
-strtok_r(char * __restrict s, const char * __restrict delim, char ** __restrict last)
-{
- char *spanp, *tok;
- int c, sc;
-
- if (s == NULL && (s = *last) == NULL)
- return (NULL);
-
- /*
- * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
- */
-cont:
- c = *s++;
- for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
- if (c == sc)
- goto cont;
- }
-
- if (c == 0) { /* no non-delimiter characters */
- *last = NULL;
- return (NULL);
- }
- tok = s - 1;
-
- /*
- * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
- * Note that delim must have one NUL; we stop if we see that, too.
- */
- for (;;) {
- c = *s++;
- spanp = (char *)delim;
- do {
- if ((sc = *spanp++) == c) {
- if (c == 0)
- s = NULL;
- else
- s[-1] = '\0';
- *last = s;
- return (tok);
- }
- } while (sc != 0);
- }
- /* NOTREACHED */
-}
diff --git a/lib/libc/musl/src/string/bcopy.c b/lib/libc/musl/src/string/bcopy.c
@@ -1,8 +0,0 @@
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-void bcopy(const void *s1, void *s2, size_t n)
-{
- memmove(s2, s1, n);
-}
diff --git a/lib/libc/musl/src/string/explicit_bzero.c b/lib/libc/musl/src/string/explicit_bzero.c
@@ -1,8 +0,0 @@
-#define _BSD_SOURCE
-#include <string.h>
-
-void explicit_bzero(void *d, size_t n)
-{
- d = memset(d, 0, n);
- __asm__ __volatile__ ("" : : "r"(d) : "memory");
-}
diff --git a/lib/libc/musl/src/string/index.c b/lib/libc/musl/src/string/index.c
@@ -1,8 +0,0 @@
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-char *index(const char *s, int c)
-{
- return strchr(s, c);
-}
diff --git a/lib/libc/musl/src/string/memccpy.c b/lib/libc/musl/src/string/memccpy.c
@@ -1,34 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-void *memccpy(void *restrict dest, const void *restrict src, int c, size_t n)
-{
- unsigned char *d = dest;
- const unsigned char *s = src;
-
- c = (unsigned char)c;
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- word *wd;
- const word *ws;
- if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
- for (; ((uintptr_t)s & ALIGN) && n && (*d=*s)!=c; n--, s++, d++);
- if ((uintptr_t)s & ALIGN) goto tail;
- size_t k = ONES * c;
- wd=(void *)d; ws=(const void *)s;
- for (; n>=sizeof(size_t) && !HASZERO(*ws^k);
- n-=sizeof(size_t), ws++, wd++) *wd = *ws;
- d=(void *)wd; s=(const void *)ws;
- }
-#endif
- for (; n && (*d=*s)!=c; n--, s++, d++);
-tail:
- if (n) return d+1;
- return 0;
-}
diff --git a/lib/libc/musl/src/string/memchr.c b/lib/libc/musl/src/string/memchr.c
@@ -1,27 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define SS (sizeof(size_t))
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-void *memchr(const void *src, int c, size_t n)
-{
- const unsigned char *s = src;
- c = (unsigned char)c;
-#ifdef __GNUC__
- for (; ((uintptr_t)s & ALIGN) && n && *s != c; s++, n--);
- if (n && *s != c) {
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- size_t k = ONES * c;
- for (w = (const void *)s; n>=SS && !HASZERO(*w^k); w++, n-=SS);
- s = (const void *)w;
- }
-#endif
- for (; n && *s != c; s++, n--);
- return n ? (void *)s : 0;
-}
diff --git a/lib/libc/musl/src/string/memmem.c b/lib/libc/musl/src/string/memmem.c
@@ -1,149 +0,0 @@
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdint.h>
-
-static char *twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
- for (h+=2, k-=2; k; k--, hw = hw<<8 | *h++)
- if (hw == nw) return (char *)h-2;
- return hw == nw ? (char *)h-2 : 0;
-}
-
-static char *threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8;
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8;
- for (h+=3, k-=3; k; k--, hw = (hw|*h++)<<8)
- if (hw == nw) return (char *)h-3;
- return hw == nw ? (char *)h-3 : 0;
-}
-
-static char *fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
- for (h+=4, k-=4; k; k--, hw = hw<<8 | *h++)
- if (hw == nw) return (char *)h-4;
- return hw == nw ? (char *)h-4 : 0;
-}
-
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define MIN(a,b) ((a)<(b)?(a):(b))
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-static char *twoway_memmem(const unsigned char *h, const unsigned char *z, const unsigned char *n, size_t l)
-{
- size_t i, ip, jp, k, p, ms, p0, mem, mem0;
- size_t byteset[32 / sizeof(size_t)] = { 0 };
- size_t shift[256];
-
- /* Computing length of needle and fill shift table */
- for (i=0; i<l; i++)
- BITOP(byteset, n[i], |=), shift[n[i]] = i+1;
-
- /* Compute maximal suffix */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] > n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- ms = ip;
- p0 = p;
-
- /* And with the opposite comparison */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] < n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- if (ip+1 > ms+1) ms = ip;
- else p = p0;
-
- /* Periodic needle? */
- if (memcmp(n, n+p, ms+1)) {
- mem0 = 0;
- p = MAX(ms, l-ms-1) + 1;
- } else mem0 = l-p;
- mem = 0;
-
- /* Search loop */
- for (;;) {
- /* If remainder of haystack is shorter than needle, done */
- if (z-h < l) return 0;
-
- /* Check last byte first; advance by shift on mismatch */
- if (BITOP(byteset, h[l-1], &)) {
- k = l-shift[h[l-1]];
- if (k) {
- if (k < mem) k = mem;
- h += k;
- mem = 0;
- continue;
- }
- } else {
- h += l;
- mem = 0;
- continue;
- }
-
- /* Compare right half */
- for (k=MAX(ms+1,mem); k<l && n[k] == h[k]; k++);
- if (k < l) {
- h += k-ms;
- mem = 0;
- continue;
- }
- /* Compare left half */
- for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
- if (k <= mem) return (char *)h;
- h += p;
- mem = mem0;
- }
-}
-
-void *memmem(const void *h0, size_t k, const void *n0, size_t l)
-{
- const unsigned char *h = h0, *n = n0;
-
- /* Return immediately on empty needle */
- if (!l) return (void *)h;
-
- /* Return immediately when needle is longer than haystack */
- if (k<l) return 0;
-
- /* Use faster algorithms for short needles */
- h = memchr(h0, *n, k);
- if (!h || l==1) return (void *)h;
- k -= h - (const unsigned char *)h0;
- if (k<l) return 0;
- if (l==2) return twobyte_memmem(h, k, n);
- if (l==3) return threebyte_memmem(h, k, n);
- if (l==4) return fourbyte_memmem(h, k, n);
-
- return twoway_memmem(h, h+k, n, l);
-}
diff --git a/lib/libc/musl/src/string/mempcpy.c b/lib/libc/musl/src/string/mempcpy.c
@@ -1,7 +0,0 @@
-#define _GNU_SOURCE
-#include <string.h>
-
-void *mempcpy(void *dest, const void *src, size_t n)
-{
- return (char *)memcpy(dest, src, n) + n;
-}
diff --git a/lib/libc/musl/src/string/memrchr.c b/lib/libc/musl/src/string/memrchr.c
@@ -1,11 +0,0 @@
-#include <string.h>
-
-void *__memrchr(const void *m, int c, size_t n)
-{
- const unsigned char *s = m;
- c = (unsigned char)c;
- while (n--) if (s[n]==c) return (void *)(s+n);
- return 0;
-}
-
-weak_alias(__memrchr, memrchr);
diff --git a/lib/libc/musl/src/string/rindex.c b/lib/libc/musl/src/string/rindex.c
@@ -1,8 +0,0 @@
-#define _BSD_SOURCE
-#include <string.h>
-#include <strings.h>
-
-char *rindex(const char *s, int c)
-{
- return strrchr(s, c);
-}
diff --git a/lib/libc/musl/src/string/stpcpy.c b/lib/libc/musl/src/string/stpcpy.c
@@ -1,29 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t))
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__stpcpy(char *restrict d, const char *restrict s)
-{
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- word *wd;
- const word *ws;
- if ((uintptr_t)s % ALIGN == (uintptr_t)d % ALIGN) {
- for (; (uintptr_t)s % ALIGN; s++, d++)
- if (!(*d=*s)) return d;
- wd=(void *)d; ws=(const void *)s;
- for (; !HASZERO(*ws); *wd++ = *ws++);
- d=(void *)wd; s=(const void *)ws;
- }
-#endif
- for (; (*d=*s); s++, d++);
-
- return d;
-}
-
-weak_alias(__stpcpy, stpcpy);
diff --git a/lib/libc/musl/src/string/stpncpy.c b/lib/libc/musl/src/string/stpncpy.c
@@ -1,32 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__stpncpy(char *restrict d, const char *restrict s, size_t n)
-{
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- word *wd;
- const word *ws;
- if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
- for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++);
- if (!n || !*s) goto tail;
- wd=(void *)d; ws=(const void *)s;
- for (; n>=sizeof(size_t) && !HASZERO(*ws);
- n-=sizeof(size_t), ws++, wd++) *wd = *ws;
- d=(void *)wd; s=(const void *)ws;
- }
-#endif
- for (; n && (*d=*s); n--, s++, d++);
-tail:
- memset(d, 0, n);
- return d;
-}
-
-weak_alias(__stpncpy, stpncpy);
-
diff --git a/lib/libc/musl/src/string/strcasestr.c b/lib/libc/musl/src/string/strcasestr.c
@@ -1,9 +0,0 @@
-#define _GNU_SOURCE
-#include <string.h>
-
-char *strcasestr(const char *h, const char *n)
-{
- size_t l = strlen(n);
- for (; *h; h++) if (!strncasecmp(h, n, l)) return (char *)h;
- return 0;
-}
diff --git a/lib/libc/musl/src/string/strcat.c b/lib/libc/musl/src/string/strcat.c
@@ -1,7 +0,0 @@
-#include <string.h>
-
-char *strcat(char *restrict dest, const char *restrict src)
-{
- strcpy(dest + strlen(dest), src);
- return dest;
-}
diff --git a/lib/libc/musl/src/string/strchr.c b/lib/libc/musl/src/string/strchr.c
@@ -1,7 +0,0 @@
-#include <string.h>
-
-char *strchr(const char *s, int c)
-{
- char *r = __strchrnul(s, c);
- return *(unsigned char *)r == (unsigned char)c ? r : 0;
-}
diff --git a/lib/libc/musl/src/string/strchrnul.c b/lib/libc/musl/src/string/strchrnul.c
@@ -1,28 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t))
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__strchrnul(const char *s, int c)
-{
- c = (unsigned char)c;
- if (!c) return (char *)s + strlen(s);
-
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- for (; (uintptr_t)s % ALIGN; s++)
- if (!*s || *(unsigned char *)s == c) return (char *)s;
- size_t k = ONES * c;
- for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
- s = (void *)w;
-#endif
- for (; *s && *(unsigned char *)s != c; s++);
- return (char *)s;
-}
-
-weak_alias(__strchrnul, strchrnul);
diff --git a/lib/libc/musl/src/string/strcpy.c b/lib/libc/musl/src/string/strcpy.c
@@ -1,7 +0,0 @@
-#include <string.h>
-
-char *strcpy(char *restrict dest, const char *restrict src)
-{
- __stpcpy(dest, src);
- return dest;
-}
diff --git a/lib/libc/musl/src/string/strcspn.c b/lib/libc/musl/src/string/strcspn.c
@@ -1,17 +0,0 @@
-#include <string.h>
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-size_t strcspn(const char *s, const char *c)
-{
- const char *a = s;
- size_t byteset[32/sizeof(size_t)];
-
- if (!c[0] || !c[1]) return __strchrnul(s, *c)-a;
-
- memset(byteset, 0, sizeof byteset);
- for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++);
- for (; *s && !BITOP(byteset, *(unsigned char *)s, &); s++);
- return s-a;
-}
diff --git a/lib/libc/musl/src/string/strlcat.c b/lib/libc/musl/src/string/strlcat.c
@@ -1,9 +0,0 @@
-#define _BSD_SOURCE
-#include <string.h>
-
-size_t strlcat(char *d, const char *s, size_t n)
-{
- size_t l = strnlen(d, n);
- if (l == n) return l + strlen(s);
- return l + strlcpy(d+l, s, n-l);
-}
diff --git a/lib/libc/musl/src/string/strlcpy.c b/lib/libc/musl/src/string/strlcpy.c
@@ -1,34 +0,0 @@
-#define _BSD_SOURCE
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-size_t strlcpy(char *d, const char *s, size_t n)
-{
- char *d0 = d;
- size_t *wd;
-
- if (!n--) goto finish;
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- const word *ws;
- if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
- for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++);
- if (n && *s) {
- wd=(void *)d; ws=(const void *)s;
- for (; n>=sizeof(size_t) && !HASZERO(*ws);
- n-=sizeof(size_t), ws++, wd++) *wd = *ws;
- d=(void *)wd; s=(const void *)ws;
- }
- }
-#endif
- for (; n && (*d=*s); n--, s++, d++);
- *d = 0;
-finish:
- return d-d0 + strlen(s);
-}
diff --git a/lib/libc/musl/src/string/strncat.c b/lib/libc/musl/src/string/strncat.c
@@ -1,10 +0,0 @@
-#include <string.h>
-
-char *strncat(char *restrict d, const char *restrict s, size_t n)
-{
- char *a = d;
- d += strlen(d);
- while (n && *s) n--, *d++ = *s++;
- *d++ = 0;
- return a;
-}
diff --git a/lib/libc/musl/src/string/strncpy.c b/lib/libc/musl/src/string/strncpy.c
@@ -1,7 +0,0 @@
-#include <string.h>
-
-char *strncpy(char *restrict d, const char *restrict s, size_t n)
-{
- __stpncpy(d, s, n);
- return d;
-}
diff --git a/lib/libc/musl/src/string/strnlen.c b/lib/libc/musl/src/string/strnlen.c
@@ -1,7 +0,0 @@
-#include <string.h>
-
-size_t strnlen(const char *s, size_t n)
-{
- const char *p = memchr(s, 0, n);
- return p ? p-s : n;
-}
diff --git a/lib/libc/musl/src/string/strpbrk.c b/lib/libc/musl/src/string/strpbrk.c
@@ -1,7 +0,0 @@
-#include <string.h>
-
-char *strpbrk(const char *s, const char *b)
-{
- s += strcspn(s, b);
- return *s ? (char *)s : 0;
-}
diff --git a/lib/libc/musl/src/string/strrchr.c b/lib/libc/musl/src/string/strrchr.c
@@ -1,6 +0,0 @@
-#include <string.h>
-
-char *strrchr(const char *s, int c)
-{
- return __memrchr(s, c, strlen(s) + 1);
-}
diff --git a/lib/libc/musl/src/string/strsep.c b/lib/libc/musl/src/string/strsep.c
@@ -1,13 +0,0 @@
-#define _GNU_SOURCE
-#include <string.h>
-
-char *strsep(char **str, const char *sep)
-{
- char *s = *str, *end;
- if (!s) return NULL;
- end = s + strcspn(s, sep);
- if (*end) *end++ = 0;
- else end = 0;
- *str = end;
- return s;
-}
diff --git a/lib/libc/musl/src/string/strspn.c b/lib/libc/musl/src/string/strspn.c
@@ -1,20 +0,0 @@
-#include <string.h>
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-size_t strspn(const char *s, const char *c)
-{
- const char *a = s;
- size_t byteset[32/sizeof(size_t)] = { 0 };
-
- if (!c[0]) return 0;
- if (!c[1]) {
- for (; *s == *c; s++);
- return s-a;
- }
-
- for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++);
- for (; *s && BITOP(byteset, *(unsigned char *)s, &); s++);
- return s-a;
-}
diff --git a/lib/libc/musl/src/string/strstr.c b/lib/libc/musl/src/string/strstr.c
@@ -1,154 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-
-static char *twobyte_strstr(const unsigned char *h, const unsigned char *n)
-{
- uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
- for (h++; *h && hw != nw; hw = hw<<8 | *++h);
- return *h ? (char *)h-1 : 0;
-}
-
-static char *threebyte_strstr(const unsigned char *h, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8;
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8;
- for (h+=2; *h && hw != nw; hw = (hw|*++h)<<8);
- return *h ? (char *)h-2 : 0;
-}
-
-static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n)
-{
- uint32_t nw = (uint32_t)n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
- uint32_t hw = (uint32_t)h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
- for (h+=3; *h && hw != nw; hw = hw<<8 | *++h);
- return *h ? (char *)h-3 : 0;
-}
-
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define MIN(a,b) ((a)<(b)?(a):(b))
-
-#define BITOP(a,b,op) \
- ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
-
-static char *twoway_strstr(const unsigned char *h, const unsigned char *n)
-{
- const unsigned char *z;
- size_t l, ip, jp, k, p, ms, p0, mem, mem0;
- size_t byteset[32 / sizeof(size_t)] = { 0 };
- size_t shift[256];
-
- /* Computing length of needle and fill shift table */
- for (l=0; n[l] && h[l]; l++)
- BITOP(byteset, n[l], |=), shift[n[l]] = l+1;
- if (n[l]) return 0; /* hit the end of h */
-
- /* Compute maximal suffix */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] > n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- ms = ip;
- p0 = p;
-
- /* And with the opposite comparison */
- ip = -1; jp = 0; k = p = 1;
- while (jp+k<l) {
- if (n[ip+k] == n[jp+k]) {
- if (k == p) {
- jp += p;
- k = 1;
- } else k++;
- } else if (n[ip+k] < n[jp+k]) {
- jp += k;
- k = 1;
- p = jp - ip;
- } else {
- ip = jp++;
- k = p = 1;
- }
- }
- if (ip+1 > ms+1) ms = ip;
- else p = p0;
-
- /* Periodic needle? */
- if (memcmp(n, n+p, ms+1)) {
- mem0 = 0;
- p = MAX(ms, l-ms-1) + 1;
- } else mem0 = l-p;
- mem = 0;
-
- /* Initialize incremental end-of-haystack pointer */
- z = h;
-
- /* Search loop */
- for (;;) {
- /* Update incremental end-of-haystack pointer */
- if (z-h < l) {
- /* Fast estimate for MAX(l,63) */
- size_t grow = l | 63;
- const unsigned char *z2 = memchr(z, 0, grow);
- if (z2) {
- z = z2;
- if (z-h < l) return 0;
- } else z += grow;
- }
-
- /* Check last byte first; advance by shift on mismatch */
- if (BITOP(byteset, h[l-1], &)) {
- k = l-shift[h[l-1]];
- if (k) {
- if (k < mem) k = mem;
- h += k;
- mem = 0;
- continue;
- }
- } else {
- h += l;
- mem = 0;
- continue;
- }
-
- /* Compare right half */
- for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++);
- if (n[k]) {
- h += k-ms;
- mem = 0;
- continue;
- }
- /* Compare left half */
- for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
- if (k <= mem) return (char *)h;
- h += p;
- mem = mem0;
- }
-}
-
-char *strstr(const char *h, const char *n)
-{
- /* Return immediately on empty needle */
- if (!n[0]) return (char *)h;
-
- /* Use faster algorithms for short needles */
- h = strchr(h, *n);
- if (!h || !n[1]) return (char *)h;
- if (!h[1]) return 0;
- if (!n[2]) return twobyte_strstr((void *)h, (void *)n);
- if (!h[2]) return 0;
- if (!n[3]) return threebyte_strstr((void *)h, (void *)n);
- if (!h[3]) return 0;
- if (!n[4]) return fourbyte_strstr((void *)h, (void *)n);
-
- return twoway_strstr((void *)h, (void *)n);
-}
diff --git a/lib/libc/musl/src/string/strtok.c b/lib/libc/musl/src/string/strtok.c
@@ -1,13 +0,0 @@
-#include <string.h>
-
-char *strtok(char *restrict s, const char *restrict sep)
-{
- static char *p;
- if (!s && !(s = p)) return NULL;
- s += strspn(s, sep);
- if (!*s) return p = 0;
- p = s + strcspn(s, sep);
- if (*p) *p++ = 0;
- else p = 0;
- return s;
-}
diff --git a/lib/libc/musl/src/string/strtok_r.c b/lib/libc/musl/src/string/strtok_r.c
@@ -1,12 +0,0 @@
-#include <string.h>
-
-char *strtok_r(char *restrict s, const char *restrict sep, char **restrict p)
-{
- if (!s && !(s = *p)) return NULL;
- s += strspn(s, sep);
- if (!*s) return *p = 0;
- *p = s + strcspn(s, sep);
- if (**p) *(*p)++ = 0;
- else *p = 0;
- return s;
-}
diff --git a/lib/libc/musl/src/string/wcstok.c b/lib/libc/musl/src/string/wcstok.c
@@ -1,12 +0,0 @@
-#include <wchar.h>
-
-wchar_t *wcstok(wchar_t *restrict s, const wchar_t *restrict sep, wchar_t **restrict p)
-{
- if (!s && !(s = *p)) return NULL;
- s += wcsspn(s, sep);
- if (!*s) return *p = 0;
- *p = s + wcscspn(s, sep);
- if (**p) *(*p)++ = 0;
- else *p = 0;
- return s;
-}
diff --git a/lib/libc/wasi/libc-top-half/musl/src/string/memchr.c b/lib/libc/wasi/libc-top-half/musl/src/string/memchr.c
@@ -1,89 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#ifdef __wasm_simd128__
-#include <wasm_simd128.h>
-#endif
-
-#define SS (sizeof(size_t))
-#define ALIGN (sizeof(size_t)-1)
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-void *memchr(const void *src, int c, size_t n)
-{
-#if defined(__wasm_simd128__) && defined(__wasilibc_simd_string)
- // Skip Clang 19 and Clang 20 which have a bug (llvm/llvm-project#146574)
- // which results in an ICE when inline assembly is used with a vector result.
-#if __clang_major__ != 19 && __clang_major__ != 20
- // When n is zero, a function that locates a character finds no occurrence.
- // Otherwise, decrement n to ensure sub_overflow overflows
- // when n would go equal-to-or-below zero.
- if (!n--) {
- return NULL;
- }
-
- // Note that reading before/after the allocation of a pointer is UB in
- // C, so inline assembly is used to generate the exact machine
- // instruction we want with opaque semantics to the compiler to avoid
- // the UB.
- uintptr_t align = (uintptr_t)src % sizeof(v128_t);
- uintptr_t addr = (uintptr_t)src - align;
- v128_t vc = wasm_i8x16_splat(c);
-
- for (;;) {
- v128_t v;
- __asm__ (
- "local.get %1\n"
- "v128.load 0\n"
- "local.set %0\n"
- : "=r"(v)
- : "r"(addr)
- : "memory");
- v128_t cmp = wasm_i8x16_eq(v, vc);
- // Bitmask is slow on AArch64, any_true is much faster.
- if (wasm_v128_any_true(cmp)) {
- // Clear the bits corresponding to align (little-endian)
- // so we can count trailing zeros.
- int mask = wasm_i8x16_bitmask(cmp) >> align << align;
- // At least one bit will be set, unless align cleared them.
- // Knowing this helps the compiler if it unrolls the loop.
- __builtin_assume(mask || align);
- // If the mask became zero because of align,
- // it's as if we didn't find anything.
- if (mask) {
- // Find the offset of the first one bit (little-endian).
- // That's a match, unless it is beyond the end of the object.
- // Recall that we decremented n, so less-than-or-equal-to is correct.
- size_t ctz = __builtin_ctz(mask);
- return ctz - align <= n ? (char *)src + (addr + ctz - (uintptr_t)src)
- : NULL;
- }
- }
- // Decrement n; if it overflows we're done.
- if (__builtin_sub_overflow(n, sizeof(v128_t) - align, &n)) {
- return NULL;
- }
- align = 0;
- addr += sizeof(v128_t);
- }
-#endif
-#endif
-
- const unsigned char *s = src;
- c = (unsigned char)c;
-#ifdef __GNUC__
- for (; ((uintptr_t)s & ALIGN) && n && *s != c; s++, n--);
- if (n && *s != c) {
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- size_t k = ONES * c;
- for (w = (const void *)s; n>=SS && !HASZERO(*w^k); w++, n-=SS);
- s = (const void *)w;
- }
-#endif
- for (; n && *s != c; s++, n--);
- return n ? (void *)s : 0;
-}
diff --git a/lib/libc/wasi/libc-top-half/musl/src/string/memrchr.c b/lib/libc/wasi/libc-top-half/musl/src/string/memrchr.c
@@ -1,33 +0,0 @@
-#include <string.h>
-
-#ifdef __wasm_simd128__
-#include <wasm_simd128.h>
-#endif
-
-void *__memrchr(const void *m, int c, size_t n)
-{
-#if defined(__wasm_simd128__) && defined(__wasilibc_simd_string)
- // memrchr is allowed to read up to n bytes from the object.
- // Search backward for the last matching character.
- const v128_t *v = (v128_t *)((char *)m + n);
- const v128_t vc = wasm_i8x16_splat(c);
- for (; n >= sizeof(v128_t); n -= sizeof(v128_t)) {
- const v128_t cmp = wasm_i8x16_eq(wasm_v128_load(--v), vc);
- // Bitmask is slow on AArch64, any_true is much faster.
- if (wasm_v128_any_true(cmp)) {
- // Find the offset of the last one bit (little-endian).
- // The leading 16 bits of the bitmask are always zero,
- // and to be ignored.
- size_t clz = __builtin_clz(wasm_i8x16_bitmask(cmp)) - 16;
- return (char *)(v + 1) - (clz + 1);
- }
- }
-#endif
-
- const unsigned char *s = m;
- c = (unsigned char)c;
- while (n--) if (s[n]==c) return (void *)(s+n);
- return 0;
-}
-
-weak_alias(__memrchr, memrchr);
diff --git a/lib/libc/wasi/libc-top-half/musl/src/string/strchrnul.c b/lib/libc/wasi/libc-top-half/musl/src/string/strchrnul.c
@@ -1,75 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <limits.h>
-
-#ifdef __wasm_simd128__
-#include <wasm_simd128.h>
-#endif
-
-#define ALIGN (sizeof(size_t))
-#define ONES ((size_t)-1/UCHAR_MAX)
-#define HIGHS (ONES * (UCHAR_MAX/2+1))
-#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
-
-char *__strchrnul(const char *s, int c)
-{
- c = (unsigned char)c;
- if (!c) return (char *)s + strlen(s);
-
-#if defined(__wasm_simd128__) && defined(__wasilibc_simd_string)
- // Skip Clang 19 and Clang 20 which have a bug (llvm/llvm-project#146574)
- // which results in an ICE when inline assembly is used with a vector result.
-#if __clang_major__ != 19 && __clang_major__ != 20
- // Note that reading before/after the allocation of a pointer is UB in
- // C, so inline assembly is used to generate the exact machine
- // instruction we want with opaque semantics to the compiler to avoid
- // the UB.
- uintptr_t align = (uintptr_t)s % sizeof(v128_t);
- uintptr_t addr = (uintptr_t)s - align;
- v128_t vc = wasm_i8x16_splat(c);
-
- for (;;) {
- v128_t v;
- __asm__ (
- "local.get %1\n"
- "v128.load 0\n"
- "local.set %0\n"
- : "=r"(v)
- : "r"(addr)
- : "memory");
- const v128_t cmp = wasm_i8x16_eq(v, (v128_t){}) | wasm_i8x16_eq(v, vc);
- // Bitmask is slow on AArch64, any_true is much faster.
- if (wasm_v128_any_true(cmp)) {
- // Clear the bits corresponding to align (little-endian)
- // so we can count trailing zeros.
- int mask = wasm_i8x16_bitmask(cmp) >> align << align;
- // At least one bit will be set, unless align cleared them.
- // Knowing this helps the compiler if it unrolls the loop.
- __builtin_assume(mask || align);
- // If the mask became zero because of align,
- // it's as if we didn't find anything.
- if (mask) {
- // Find the offset of the first one bit (little-endian).
- return (char *)s + (addr - (uintptr_t)s + __builtin_ctz(mask));
- }
- }
- align = 0;
- addr += sizeof(v128_t);
- }
-#endif
-#endif
-
-#ifdef __GNUC__
- typedef size_t __attribute__((__may_alias__)) word;
- const word *w;
- for (; (uintptr_t)s % ALIGN; s++)
- if (!*s || *(unsigned char *)s == c) return (char *)s;
- size_t k = ONES * c;
- for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
- s = (void *)w;
-#endif
- for (; *s && *(unsigned char *)s != c; s++);
- return (char *)s;
-}
-
-weak_alias(__strchrnul, strchrnul);
diff --git a/lib/std/ascii.zig b/lib/std/ascii.zig
@@ -464,6 +464,30 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {
return std.math.order(lhs.len, rhs.len);
}
+/// Returns the lexicographical order of two many-item pointers with NUL-termination. O(n).
+pub fn orderIgnoreCaseZ(lhs: [*:0]const u8, rhs: [*:0]const u8) std.math.Order {
+ return boundedOrderIgnoreCaseZ(lhs, rhs, std.math.maxInt(usize));
+}
+
+test orderIgnoreCaseZ {
+ try std.testing.expect(orderIgnoreCaseZ("aBcD", "Bee") == .lt);
+ try std.testing.expect(orderIgnoreCaseZ("AbC", "aBc") == .eq);
+ try std.testing.expect(orderIgnoreCaseZ("abC", "aBc0") == .lt);
+ try std.testing.expect(orderIgnoreCaseZ("", "") == .eq);
+ try std.testing.expect(orderIgnoreCaseZ("", "a") == .lt);
+
+ const s: [*:0]const u8 = "Abc";
+ try std.testing.expect(orderIgnoreCaseZ(s, s) == .eq);
+}
+
+/// Returns the lexicographical order of two many-item pointers with NUL-termination until some specified bound. O(n).
+pub fn boundedOrderIgnoreCaseZ(lhs: [*:0]const u8, rhs: [*:0]const u8, bound: usize) std.math.Order {
+ if (lhs == rhs) return .eq;
+ var i: usize = 0;
+ while (i < bound and toLower(lhs[i]) == toLower(rhs[i]) and lhs[i] != 0) : (i += 1) {}
+ return if (i < bound) std.math.order(toLower(lhs[i]), toLower(rhs[i])) else .eq;
+}
+
/// Returns whether the lexicographical order of `lhs` is lower than `rhs`.
pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
return orderIgnoreCase(lhs, rhs) == .lt;
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
@@ -662,10 +662,15 @@ pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {
/// Compares two many-item pointers with NUL-termination lexicographically.
pub fn orderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) math.Order {
+ return boundedOrderZ(T, lhs, rhs, std.math.maxInt(usize));
+}
+
+/// Compares two many-item pointers with NUL-termination lexicographically until some specified bound.
+pub fn boundedOrderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T, bound: usize) math.Order {
if (lhs == rhs) return .eq;
var i: usize = 0;
- while (lhs[i] == rhs[i] and lhs[i] != 0) : (i += 1) {}
- return math.order(lhs[i], rhs[i]);
+ while (lhs[i] == rhs[i] and lhs[i] != 0 and i < bound) : (i += 1) {}
+ return if (i < bound) math.order(lhs[i], rhs[i]) else .eq;
}
test order {
diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig
@@ -661,7 +661,6 @@ const mingw32_generic_src = [_][]const u8{
"misc" ++ path.sep_str ++ "getlogin.c",
"misc" ++ path.sep_str ++ "getopt.c",
"misc" ++ path.sep_str ++ "gettimeofday.c",
- "misc" ++ path.sep_str ++ "mempcpy.c",
"misc" ++ path.sep_str ++ "mingw-access.c",
"misc" ++ path.sep_str ++ "mingw-aligned-malloc.c",
"misc" ++ path.sep_str ++ "mingw_getsp.S",
@@ -674,7 +673,6 @@ const mingw32_generic_src = [_][]const u8{
"misc" ++ path.sep_str ++ "mingw_wcstold.c",
"misc" ++ path.sep_str ++ "mkstemp.c",
"misc" ++ path.sep_str ++ "sleep.c",
- "misc" ++ path.sep_str ++ "strnlen.c",
"misc" ++ path.sep_str ++ "strsafe.c",
"misc" ++ path.sep_str ++ "tdelete.c",
"misc" ++ path.sep_str ++ "tdestroy.c",
@@ -744,7 +742,6 @@ const mingw32_generic_src = [_][]const u8{
"stdio" ++ path.sep_str ++ "mingw_vswscanf.c",
"stdio" ++ path.sep_str ++ "snprintf.c",
"stdio" ++ path.sep_str ++ "snwprintf.c",
- "stdio" ++ path.sep_str ++ "strtok_r.c",
"stdio" ++ path.sep_str ++ "truncate.c",
"stdio" ++ path.sep_str ++ "ulltoa.c",
"stdio" ++ path.sep_str ++ "ulltow.c",
diff --git a/src/libs/musl.zig b/src/libs/musl.zig
@@ -784,10 +784,8 @@ const src_files = [_][]const u8{
"musl/src/locale/newlocale.c",
"musl/src/locale/pleval.c",
"musl/src/locale/setlocale.c",
- "musl/src/locale/strcoll.c",
"musl/src/locale/strfmon.c",
"musl/src/locale/strtod_l.c",
- "musl/src/locale/strxfrm.c",
"musl/src/locale/textdomain.c",
"musl/src/locale/uselocale.c",
"musl/src/locale/wcscoll.c",
@@ -1129,9 +1127,6 @@ const src_files = [_][]const u8{
"musl/src/misc/a64l.c",
"musl/src/misc/basename.c",
"musl/src/misc/dirname.c",
- "musl/src/misc/ffs.c",
- "musl/src/misc/ffsl.c",
- "musl/src/misc/ffsll.c",
"musl/src/misc/fmtmsg.c",
"musl/src/misc/forkpty.c",
"musl/src/misc/getauxval.c",
@@ -1616,39 +1611,10 @@ const src_files = [_][]const u8{
"musl/src/stdlib/strtod.c",
"musl/src/stdlib/wcstod.c",
"musl/src/stdlib/wcstol.c",
- "musl/src/string/bcopy.c",
- "musl/src/string/explicit_bzero.c",
- "musl/src/string/index.c",
- "musl/src/string/memccpy.c",
- "musl/src/string/memchr.c",
- "musl/src/string/memmem.c",
- "musl/src/string/mempcpy.c",
- "musl/src/string/memrchr.c",
- "musl/src/string/rindex.c",
- "musl/src/string/stpcpy.c",
- "musl/src/string/stpncpy.c",
- "musl/src/string/strcasestr.c",
- "musl/src/string/strcat.c",
- "musl/src/string/strchr.c",
- "musl/src/string/strchrnul.c",
- "musl/src/string/strcpy.c",
- "musl/src/string/strcspn.c",
"musl/src/string/strdup.c",
"musl/src/string/strerror_r.c",
- "musl/src/string/strlcat.c",
- "musl/src/string/strlcpy.c",
- "musl/src/string/strncat.c",
- "musl/src/string/strncpy.c",
"musl/src/string/strndup.c",
- "musl/src/string/strnlen.c",
- "musl/src/string/strpbrk.c",
- "musl/src/string/strrchr.c",
- "musl/src/string/strsep.c",
"musl/src/string/strsignal.c",
- "musl/src/string/strspn.c",
- "musl/src/string/strstr.c",
- "musl/src/string/strtok.c",
- "musl/src/string/strtok_r.c",
"musl/src/string/strverscmp.c",
"musl/src/string/swab.c",
"musl/src/string/wcscasecmp.c",
@@ -1656,7 +1622,6 @@ const src_files = [_][]const u8{
"musl/src/string/wcsdup.c",
"musl/src/string/wcsncasecmp.c",
"musl/src/string/wcsncasecmp_l.c",
- "musl/src/string/wcstok.c",
"musl/src/temp/mkdtemp.c",
"musl/src/temp/mkostemp.c",
"musl/src/temp/mkostemps.c",
diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig
@@ -680,10 +680,8 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/locale/__mo_lookup.c",
"musl/src/locale/pleval.c",
"musl/src/locale/setlocale.c",
- "musl/src/locale/strcoll.c",
"musl/src/locale/strfmon.c",
"musl/src/locale/strtod_l.c",
- "musl/src/locale/strxfrm.c",
"musl/src/locale/wcscoll.c",
"musl/src/locale/wcsxfrm.c",
"musl/src/math/acos.c",
@@ -847,9 +845,6 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/misc/a64l.c",
"musl/src/misc/basename.c",
"musl/src/misc/dirname.c",
- "musl/src/misc/ffs.c",
- "musl/src/misc/ffsl.c",
- "musl/src/misc/ffsll.c",
"musl/src/misc/getdomainname.c",
"musl/src/misc/gethostid.c",
"musl/src/misc/getopt.c",
@@ -968,35 +963,9 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/stdlib/ecvt.c",
"musl/src/stdlib/fcvt.c",
"musl/src/stdlib/gcvt.c",
- "musl/src/string/bcopy.c",
- "musl/src/string/explicit_bzero.c",
- "musl/src/string/index.c",
- "musl/src/string/memccpy.c",
- "musl/src/string/memmem.c",
- "musl/src/string/mempcpy.c",
- "musl/src/string/rindex.c",
- "musl/src/string/stpcpy.c",
- "musl/src/string/stpncpy.c",
- "musl/src/string/strcasestr.c",
- "musl/src/string/strcat.c",
- "musl/src/string/strchr.c",
- "musl/src/string/strcpy.c",
- "musl/src/string/strcspn.c",
"musl/src/string/strdup.c",
"musl/src/string/strerror_r.c",
- "musl/src/string/strlcat.c",
- "musl/src/string/strlcpy.c",
- "musl/src/string/strncat.c",
- "musl/src/string/strncpy.c",
"musl/src/string/strndup.c",
- "musl/src/string/strnlen.c",
- "musl/src/string/strpbrk.c",
- "musl/src/string/strrchr.c",
- "musl/src/string/strsep.c",
- "musl/src/string/strspn.c",
- "musl/src/string/strstr.c",
- "musl/src/string/strtok.c",
- "musl/src/string/strtok_r.c",
"musl/src/string/strverscmp.c",
"musl/src/string/swab.c",
"musl/src/string/wcscasecmp.c",
@@ -1004,7 +973,6 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/string/wcsdup.c",
"musl/src/string/wcsncasecmp.c",
"musl/src/string/wcsncasecmp_l.c",
- "musl/src/string/wcstok.c",
"musl/src/thread/default_attr.c",
"musl/src/thread/pthread_attr_destroy.c",
"musl/src/thread/pthread_attr_init.c",
@@ -1132,9 +1100,6 @@ const libc_top_half_src_files = [_][]const u8{
"wasi/libc-top-half/musl/src/stdlib/strtod.c",
"wasi/libc-top-half/musl/src/stdlib/wcstod.c",
"wasi/libc-top-half/musl/src/stdlib/wcstol.c",
- "wasi/libc-top-half/musl/src/string/memchr.c",
- "wasi/libc-top-half/musl/src/string/memrchr.c",
- "wasi/libc-top-half/musl/src/string/strchrnul.c",
"wasi/libc-top-half/musl/src/thread/pthread_attr_get.c",
"wasi/libc-top-half/musl/src/thread/pthread_attr_setguardsize.c",
"wasi/libc-top-half/musl/src/thread/pthread_attr_setschedparam.c",