| author | |
| committer | |
| log | 3ea77badf94d996740337f6ce6e6612178627dd1 |
| tree | 5e177ffd5eeeff45537e1258f0c4b5f59e4236c1 |
| parent | 22945fbbdc1ec89def58f39ddc8c35502b6e05dd |
Contributes to: #30978
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31935
Reviewed-by: Andrew Kelley <andrew@ziglang.org>11 files changed, 108 insertions(+), 38 deletions(-)
lib/c/string.zig+20| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const symbol = @import("../c.zig").symbol; | 3 | const symbol = @import("../c.zig").symbol; |
| 4 | const c = std.c; | ||
| 4 | 5 | ||
| 5 | comptime { | 6 | comptime { |
| 6 | if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { | 7 | if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { |
| ... | @@ -24,6 +25,8 @@ comptime { | ... | @@ -24,6 +25,8 @@ comptime { |
| 24 | symbol(&strpbrk, "strpbrk"); | 25 | symbol(&strpbrk, "strpbrk"); |
| 25 | symbol(&strstr, "strstr"); | 26 | symbol(&strstr, "strstr"); |
| 26 | symbol(&strtok, "strtok"); | 27 | symbol(&strtok, "strtok"); |
| 28 | symbol(&strdup, "strdup"); | ||
| 29 | symbol(&strndup, "strndup"); | ||
| 27 | // strlen is in compiler_rt | 30 | // strlen is in compiler_rt |
| 28 | 31 | ||
| 29 | symbol(&strtok_r, "strtok_r"); | 32 | symbol(&strtok_r, "strtok_r"); |
| ... | @@ -164,6 +167,23 @@ fn strtok(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char) ca | ... | @@ -164,6 +167,23 @@ fn strtok(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char) ca |
| 164 | return strtok_r(maybe_str, values, &state.str); | 167 | return strtok_r(maybe_str, values, &state.str); |
| 165 | } | 168 | } |
| 166 | 169 | ||
| 170 | fn strdup(str: [*:0]const c_char) callconv(.c) ?[*:0]c_char { | ||
| 171 | const len = std.mem.len(str); | ||
| 172 | const d_opaque = c.malloc(len + 1) orelse return null; | ||
| 173 | const d: [*]c_char = @ptrCast(d_opaque); | ||
| 174 | @memcpy(d[0 .. len + 1], str[0 .. len + 1]); | ||
| 175 | return @ptrCast(d); | ||
| 176 | } | ||
| 177 | |||
| 178 | fn strndup(str: [*:0]const c_char, n: usize) callconv(.c) ?[*:0]c_char { | ||
| 179 | const len = strnlen(str, n); | ||
| 180 | const d_opaque = c.malloc(len + 1) orelse return null; | ||
| 181 | const d: [*]c_char = @ptrCast(d_opaque); | ||
| 182 | @memcpy(d[0..len], str[0..len]); | ||
| 183 | d[len] = 0; | ||
| 184 | return @ptrCast(d); | ||
| 185 | } | ||
| 186 | |||
| 167 | // strlen is in compiler_rt | 187 | // strlen is in compiler_rt |
| 168 | 188 | ||
| 169 | 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 { | 189 | 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 { |
lib/c/wchar.zig+10| ... | @@ -5,6 +5,7 @@ const wint_t = std.c.wint_t; | ... | @@ -5,6 +5,7 @@ const wint_t = std.c.wint_t; |
| 5 | const wchar_t = std.c.wchar_t; | 5 | const wchar_t = std.c.wchar_t; |
| 6 | 6 | ||
| 7 | const symbol = @import("../c.zig").symbol; | 7 | const symbol = @import("../c.zig").symbol; |
| 8 | const c = std.c; | ||
| 8 | 9 | ||
| 9 | comptime { | 10 | comptime { |
| 10 | if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { | 11 | if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { |
| ... | @@ -30,6 +31,7 @@ comptime { | ... | @@ -30,6 +31,7 @@ comptime { |
| 30 | symbol(&wcspbrk, "wcspbrk"); | 31 | symbol(&wcspbrk, "wcspbrk"); |
| 31 | symbol(&wcstok, "wcstok"); | 32 | symbol(&wcstok, "wcstok"); |
| 32 | symbol(&wcsstr, "wcsstr"); | 33 | symbol(&wcsstr, "wcsstr"); |
| 34 | symbol(&wcsdup, "wcsdup"); | ||
| 33 | symbol(&wcswcs, "wcswcs"); | 35 | symbol(&wcswcs, "wcswcs"); |
| 34 | } | 36 | } |
| 35 | 37 | ||
| ... | @@ -189,3 +191,11 @@ fn wcsstr(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar | ... | @@ -189,3 +191,11 @@ fn wcsstr(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar |
| 189 | fn wcswcs(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar_t) callconv(.c) ?[*:0]wchar_t { | 191 | fn wcswcs(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar_t) callconv(.c) ?[*:0]wchar_t { |
| 190 | return wcsstr(haystack, needle); | 192 | return wcsstr(haystack, needle); |
| 191 | } | 193 | } |
| 194 | |||
| 195 | fn wcsdup(str: [*:0]const wchar_t) callconv(.c) ?[*:0]wchar_t { | ||
| 196 | const len = wcslen(str); | ||
| 197 | const size = (len + 1) * @sizeOf(wchar_t); | ||
| 198 | const d_opaque = c.malloc(size) orelse return null; | ||
| 199 | const d: [*]wchar_t = @ptrCast(@alignCast(d_opaque)); | ||
| 200 | return @ptrCast(wmemcpy(d, str, len + 1)); | ||
| 201 | } |
lib/libc/musl/src/string/strdup.c deleted-10| ... | @@ -1,10 +0,0 @@ | ||
| 1 | #include <stdlib.h> | ||
| 2 | #include <string.h> | ||
| 3 | |||
| 4 | char *strdup(const char *s) | ||
| 5 | { | ||
| 6 | 	size_t l = strlen(s); | ||
| 7 | 	char *d = malloc(l+1); | ||
| 8 | 	if (!d) return NULL; | ||
| 9 | 	return memcpy(d, s, l+1); | ||
| 10 | } | ||
lib/libc/musl/src/string/strndup.c deleted-12| ... | @@ -1,12 +0,0 @@ | ||
| 1 | #include <stdlib.h> | ||
| 2 | #include <string.h> | ||
| 3 | |||
| 4 | char *strndup(const char *s, size_t n) | ||
| 5 | { | ||
| 6 | 	size_t l = strnlen(s, n); | ||
| 7 | 	char *d = malloc(l+1); | ||
| 8 | 	if (!d) return NULL; | ||
| 9 | 	memcpy(d, s, l); | ||
| 10 | 	d[l] = 0; | ||
| 11 | 	return d; | ||
| 12 | } | ||
lib/libc/musl/src/string/wcsdup.c deleted-10| ... | @@ -1,10 +0,0 @@ | ||
| 1 | #include <stdlib.h> | ||
| 2 | #include <wchar.h> | ||
| 3 | |||
| 4 | wchar_t *wcsdup(const wchar_t *s) | ||
| 5 | { | ||
| 6 | 	size_t l = wcslen(s); | ||
| 7 | 	wchar_t *d = malloc((l+1)*sizeof(wchar_t)); | ||
| 8 | 	if (!d) return NULL; | ||
| 9 | 	return wmemcpy(d, s, l+1); | ||
| 10 | } | ||
lib/std/c.zig+3| ... | @@ -11136,6 +11136,9 @@ pub extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n | ... | @@ -11136,6 +11136,9 @@ pub extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n |
| 11136 | pub extern "c" fn strncmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; | 11136 | pub extern "c" fn strncmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; |
| 11137 | pub extern "c" fn strcasecmp(a: [*:0]const c_char, b: [*:0]const c_char) c_int; | 11137 | pub extern "c" fn strcasecmp(a: [*:0]const c_char, b: [*:0]const c_char) c_int; |
| 11138 | pub extern "c" fn strncasecmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; | 11138 | pub extern "c" fn strncasecmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; |
| 11139 | pub extern "c" fn strdup(s: [*:0]const c_char) ?[*:0]c_char; | ||
| 11140 | pub extern "c" fn strndup(s: [*:0]const c_char, n: usize) ?[*:0]c_char; | ||
| 11141 | pub extern "c" fn wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; | ||
| 11139 | 11142 | ||
| 11140 | pub extern "c" fn ffs(i: c_int) c_int; | 11143 | pub extern "c" fn ffs(i: c_int) c_int; |
| 11141 | pub extern "c" fn ffsl(i: c_long) c_long; | 11144 | pub extern "c" fn ffsl(i: c_long) c_long; |
src/libs/musl.zig-3| ... | @@ -1494,14 +1494,11 @@ const src_files = [_][]const u8{ | ... | @@ -1494,14 +1494,11 @@ const src_files = [_][]const u8{ |
| 1494 | "musl/src/stdlib/strtod.c", | 1494 | "musl/src/stdlib/strtod.c", |
| 1495 | "musl/src/stdlib/wcstod.c", | 1495 | "musl/src/stdlib/wcstod.c", |
| 1496 | "musl/src/stdlib/wcstol.c", | 1496 | "musl/src/stdlib/wcstol.c", |
| 1497 | "musl/src/string/strdup.c", | ||
| 1498 | "musl/src/string/strerror_r.c", | 1497 | "musl/src/string/strerror_r.c", |
| 1499 | "musl/src/string/strndup.c", | ||
| 1500 | "musl/src/string/strsignal.c", | 1498 | "musl/src/string/strsignal.c", |
| 1501 | "musl/src/string/strverscmp.c", | 1499 | "musl/src/string/strverscmp.c", |
| 1502 | "musl/src/string/wcscasecmp.c", | 1500 | "musl/src/string/wcscasecmp.c", |
| 1503 | "musl/src/string/wcscasecmp_l.c", | 1501 | "musl/src/string/wcscasecmp_l.c", |
| 1504 | "musl/src/string/wcsdup.c", | ||
| 1505 | "musl/src/string/wcsncasecmp.c", | 1502 | "musl/src/string/wcsncasecmp.c", |
| 1506 | "musl/src/string/wcsncasecmp_l.c", | 1503 | "musl/src/string/wcsncasecmp_l.c", |
| 1507 | "musl/src/temp/mkdtemp.c", | 1504 | "musl/src/temp/mkdtemp.c", |
src/libs/wasi_libc.zig-3| ... | @@ -902,13 +902,10 @@ const libc_top_half_src_files = [_][]const u8{ | ... | @@ -902,13 +902,10 @@ const libc_top_half_src_files = [_][]const u8{ |
| 902 | "musl/src/stdlib/ecvt.c", | 902 | "musl/src/stdlib/ecvt.c", |
| 903 | "musl/src/stdlib/fcvt.c", | 903 | "musl/src/stdlib/fcvt.c", |
| 904 | "musl/src/stdlib/gcvt.c", | 904 | "musl/src/stdlib/gcvt.c", |
| 905 | "musl/src/string/strdup.c", | ||
| 906 | "musl/src/string/strerror_r.c", | 905 | "musl/src/string/strerror_r.c", |
| 907 | "musl/src/string/strndup.c", | ||
| 908 | "musl/src/string/strverscmp.c", | 906 | "musl/src/string/strverscmp.c", |
| 909 | "musl/src/string/wcscasecmp.c", | 907 | "musl/src/string/wcscasecmp.c", |
| 910 | "musl/src/string/wcscasecmp_l.c", | 908 | "musl/src/string/wcscasecmp_l.c", |
| 911 | "musl/src/string/wcsdup.c", | ||
| 912 | "musl/src/string/wcsncasecmp.c", | 909 | "musl/src/string/wcsncasecmp.c", |
| 913 | "musl/src/string/wcsncasecmp_l.c", | 910 | "musl/src/string/wcsncasecmp_l.c", |
| 914 | "musl/src/thread/default_attr.c", | 911 | "musl/src/thread/default_attr.c", |
test/c.zig+1| ... | @@ -10,4 +10,5 @@ test { | ... | @@ -10,4 +10,5 @@ test { |
| 10 | _ = @import("c/string.zig"); | 10 | _ = @import("c/string.zig"); |
| 11 | _ = @import("c/strings.zig"); | 11 | _ = @import("c/strings.zig"); |
| 12 | _ = @import("c/unistd.zig"); | 12 | _ = @import("c/unistd.zig"); |
| 13 | _ = @import("c/wchar.zig"); | ||
| 13 | } | 14 | } |
test/c/string.zig+40| ... | @@ -10,3 +10,43 @@ test "strncmp" { | ... | @@ -10,3 +10,43 @@ test "strncmp" { |
| 10 | try testing.expect(c.strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); | 10 | try testing.expect(c.strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); |
| 11 | try testing.expect(c.strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0); | 11 | try testing.expect(c.strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0); |
| 12 | } | 12 | } |
| 13 | |||
| 14 | test "strdup" { | ||
| 15 | const org: [*:0]const u8 = "a"; | ||
| 16 | const cpy_opt = c.strdup(@ptrCast(org)); | ||
| 17 | const cpy = cpy_opt orelse return error.OutOfMemory; | ||
| 18 | defer c.free(cpy); | ||
| 19 | |||
| 20 | const cpy_u8: [*:0]u8 = @ptrCast(cpy); | ||
| 21 | try testing.expectEqualStrings(std.mem.span(org), std.mem.span(@as([*:0]const u8, cpy_u8))); | ||
| 22 | try testing.expect(@intFromPtr(cpy_u8) != @intFromPtr(org)); | ||
| 23 | |||
| 24 | cpy_u8[0] = 'b'; | ||
| 25 | try testing.expectEqualStrings("a", std.mem.span(org)); | ||
| 26 | try testing.expectEqualStrings("b", std.mem.span(@as([*:0]const u8, cpy_u8))); | ||
| 27 | } | ||
| 28 | |||
| 29 | test "strndup" { | ||
| 30 | if (builtin.target.os.tag == .windows) return; // no strndup | ||
| 31 | const org1: [*:0]const u8 = "Hello"; | ||
| 32 | |||
| 33 | const copy1_opt = c.strndup(@ptrCast(org1), 100); | ||
| 34 | const copy1 = copy1_opt orelse return error.OutOfMemory; | ||
| 35 | defer c.free(copy1); | ||
| 36 | const copy1_u8: [*:0]u8 = @ptrCast(copy1); | ||
| 37 | try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy1_u8))); | ||
| 38 | |||
| 39 | const org2: [*:0]const u8 = "Hello World!"; | ||
| 40 | const copy2_opt = c.strndup(@ptrCast(org2), 5); | ||
| 41 | const copy2 = copy2_opt orelse return error.OutOfMemory; | ||
| 42 | defer c.free(copy2); | ||
| 43 | const copy2_u8: [*:0]u8 = @ptrCast(copy2); | ||
| 44 | try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy2_u8))); | ||
| 45 | try testing.expectEqual(@as(usize, 5), std.mem.len(copy2_u8)); | ||
| 46 | |||
| 47 | const copy3_opt = c.strndup(@ptrCast(org1), 5); | ||
| 48 | const copy3 = copy3_opt orelse return error.OutOfMemory; | ||
| 49 | defer c.free(copy3); | ||
| 50 | const copy3_u8: [*:0]u8 = @ptrCast(copy3); | ||
| 51 | try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy3_u8))); | ||
| 52 | } |
test/c/wchar.zig created+34| ... | @@ -0,0 +1,34 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | const c = std.c; | ||
| 5 | const testing = std.testing; | ||
| 6 | |||
| 7 | test "wcsdup" { | ||
| 8 | const org: [*:0]const c.wchar_t = &[_:0]c.wchar_t{ 'H', 'e', 'l', 'l', 'o' }; | ||
| 9 | const cpy_opt = c.wcsdup(org); | ||
| 10 | const cpy = cpy_opt orelse return error.OutOfMemory; | ||
| 11 | defer c.free(cpy); | ||
| 12 | |||
| 13 | try testing.expectEqual(@as(usize, 5), std.mem.len(@as([*:0]const c.wchar_t, cpy))); | ||
| 14 | |||
| 15 | try testing.expectEqual(@as(c.wchar_t, 'H'), cpy[0]); | ||
| 16 | try testing.expectEqual(@as(c.wchar_t, 'e'), cpy[1]); | ||
| 17 | try testing.expectEqual(@as(c.wchar_t, 'l'), cpy[2]); | ||
| 18 | try testing.expectEqual(@as(c.wchar_t, 'l'), cpy[3]); | ||
| 19 | try testing.expectEqual(@as(c.wchar_t, 'o'), cpy[4]); | ||
| 20 | try testing.expectEqual(@as(c.wchar_t, 0), cpy[5]); | ||
| 21 | |||
| 22 | try testing.expect(@intFromPtr(cpy) != @intFromPtr(org)); | ||
| 23 | |||
| 24 | cpy[0] = 'B'; | ||
| 25 | try testing.expectEqual(@as(c.wchar_t, 'H'), org[0]); | ||
| 26 | try testing.expectEqual(@as(c.wchar_t, 'B'), cpy[0]); | ||
| 27 | } | ||
| 28 | |||
| 29 | test "wcsdup empty string" { | ||
| 30 | const org: [*:0]const c.wchar_t = &[_:0]c.wchar_t{}; | ||
| 31 | const cpy = c.wcsdup(org) orelse return error.OutOfMemory; | ||
| 32 | defer c.free(cpy); | ||
| 33 | try testing.expectEqual(@as(c.wchar_t, 0), cpy[0]); | ||
| 34 | } | ||