-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Implement some string functions for libzigc #23847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
39e66ee
328b64d
37e67d3
c84c5ff
df80d50
f90a588
8ef0b92
1afe953
88452ae
e5e2a58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,36 @@ const std = @import("std"); | |
const common = @import("common.zig"); | ||
|
||
comptime { | ||
@export(&strcmp, .{ .name = "strcmp", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strlen, .{ .name = "strlen", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strncmp, .{ .name = "strncmp", .linkage = common.linkage, .visibility = common.visibility }); | ||
if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { | ||
@export(&strcmp, .{ .name = "strcmp", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strlen, .{ .name = "strlen", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strnlen, .{ .name = "strnlen", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strncmp, .{ .name = "strncmp", .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(&strcpy, .{ .name = "strcpy", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strcat, .{ .name = "strcat", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strncpy, .{ .name = "strncpy", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&memccpy, .{ .name = "memccpy", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&mempcpy, .{ .name = "mempcpy", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&memmem, .{ .name = "memmem", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&memchr, .{ .name = "memchr", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strchrnul, .{ .name = "strchrnul", .linkage = .weak, .visibility = common.visibility }); | ||
@export(&memrchr, .{ .name = "memrchr", .linkage = .weak, .visibility = common.visibility }); | ||
@export(&stpcpy, .{ .name = "stpcpy", .linkage = .weak, .visibility = common.visibility }); | ||
@export(&stpncpy, .{ .name = "stpncpy", .linkage = .weak, .visibility = common.visibility }); | ||
{ | ||
// NOTE: These symbols are only depended on by other parts of the | ||
// musl c-sources. They should be removed once libzigc is further | ||
// along, and these functions have been re-implemented in zig. | ||
@export(&strchrnul, .{ .name = "__strchrnul", .linkage = .strong, .visibility = .hidden }); | ||
} | ||
} | ||
|
||
if (builtin.target.isMinGW()) { | ||
@export(&mempcpy, .{ .name = "mempcpy", .linkage = common.linkage, .visibility = common.visibility }); | ||
@export(&strnlen, .{ .name = "strnlen", .linkage = common.linkage, .visibility = common.visibility }); | ||
} | ||
} | ||
|
||
fn strcmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int { | ||
|
@@ -43,3 +70,235 @@ test strncmp { | |
fn strlen(s: [*:0]const c_char) callconv(.c) usize { | ||
return std.mem.len(s); | ||
} | ||
|
||
fn strnlen(s: [*:0]const c_char, n: usize) callconv(.c) usize { | ||
return if (std.mem.indexOfScalar(c_char, s[0..n], 0)) |idx| idx else n; | ||
} | ||
|
||
fn strchrnul(s: [*:0]const c_char, c: c_int) callconv(.c) [*:0]const c_char { | ||
const needle: c_char = @intCast(c); | ||
if (needle == 0) return s + strlen(s); | ||
|
||
var it: [*:0]const c_char = s; | ||
while (it[0] != 0 and it[0] != needle) : (it += 1) {} | ||
return it; | ||
} | ||
|
||
test strchrnul { | ||
const foo: [*:0]const c_char = @ptrCast("disco"); | ||
try std.testing.expect(strchrnul(foo, 'd') == foo); | ||
try std.testing.expect(strchrnul(foo, 'o') == (foo + 4)); | ||
try std.testing.expect(strchrnul(foo, 'z') == (foo + 5)); | ||
try std.testing.expect(strchrnul(foo, 0) == (foo + 5)); | ||
} | ||
|
||
pub fn strchr(s: [*:0]const c_char, c: c_int) callconv(.c) ?[*:0]const c_char { | ||
const result = strchrnul(s, c); | ||
const needle: c_char = @intCast(c); | ||
return if (result[0] == needle) result else null; | ||
} | ||
|
||
test strchr { | ||
const foo: [*:0]const c_char = @ptrCast("disco"); | ||
try std.testing.expect(strchr(foo, 'd') == foo); | ||
try std.testing.expect(strchr(foo, 'o') == (foo + 4)); | ||
try std.testing.expect(strchr(foo, 'z') == null); | ||
try std.testing.expect(strchr(foo, 0) == (foo + 5)); | ||
} | ||
|
||
fn memchr(m: *const anyopaque, c: c_int, n: usize) callconv(.c) ?*const anyopaque { | ||
const needle: c_char = @intCast(c); | ||
const s: [*]const c_char = @ptrCast(m); | ||
if (std.mem.indexOfScalar(c_char, s[0..n], needle)) |idx| { | ||
return @ptrCast(s + idx); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
test memchr { | ||
const foo: [*:0]const c_char = @ptrCast("disco"); | ||
try std.testing.expect(memchr(foo, 'd', 5) == @as(*const anyopaque, @ptrCast(foo))); | ||
try std.testing.expect(memchr(foo, 'o', 5) == @as(*const anyopaque, @ptrCast(foo + 4))); | ||
try std.testing.expect(memchr(foo, 'z', 5) == null); | ||
} | ||
|
||
fn memrchr(m: *const anyopaque, c: c_int, n: usize) callconv(.c) ?*const anyopaque { | ||
const needle: c_char = @intCast(c); | ||
const s: [*]const c_char = @ptrCast(m); | ||
if (std.mem.lastIndexOfScalar(c_char, s[0..n], needle)) |idx| { | ||
return @ptrCast(s + idx); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
test memrchr { | ||
const foo: [*:0]const c_char = @ptrCast("disco"); | ||
try std.testing.expect(memrchr(foo, 'd', 5) == @as(*const anyopaque, @ptrCast(foo))); | ||
try std.testing.expect(memrchr(foo, 'o', 5) == @as(*const anyopaque, @ptrCast(foo + 4))); | ||
try std.testing.expect(memrchr(foo, 'z', 5) == null); | ||
} | ||
|
||
pub fn strrchr(s: [*:0]const c_char, c: c_int) callconv(.c) ?[*:0]const c_char { | ||
return @ptrCast(memrchr(s, c, strlen(s) + 1)); | ||
} | ||
|
||
test strrchr { | ||
const foo: [*:0]const c_char = @ptrCast("disco"); | ||
try std.testing.expect(strrchr(foo, 'd') == foo); | ||
try std.testing.expect(strrchr(foo, 'o') == (foo + 4)); | ||
try std.testing.expect(strrchr(foo, 'z') == null); | ||
try std.testing.expect(strrchr(foo, 0) == (foo + 5)); | ||
} | ||
|
||
fn __aeabi_memclr(dest: *anyopaque, n: usize) callconv(.c) *anyopaque { | ||
const buf: [*]c_char = @ptrCast(dest); | ||
@memset(buf[0..n], 0); | ||
return dest; | ||
} | ||
|
||
fn memccpy(noalias dest: *anyopaque, noalias src: *const anyopaque, c: c_int, n: usize) callconv(.c) ?*anyopaque { | ||
@setRuntimeSafety(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why would this need to be different than the mode set for the compilation unit? |
||
const d: [*]c_char = @ptrCast(dest); | ||
const s: [*]const c_char = @ptrCast(src); | ||
Comment on lines
+163
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to pointer cast; you can change the parameter types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My rationale for this was that I wanted to keep the function signature as close to the c signature as possible. I was basing this decision off of a comment that you made in a prior post here. Technically in this case this would actually increase the type information, but it would not match 1:1 with the c ABI so I am unsure which approach is better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you, I forgot about that. If you don't mind, a comment would help remind future me, and future contributors as well, of the rationale behind this. |
||
const needle: c_char = @intCast(c); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a comment explaining why the |
||
var idx: usize = 0; | ||
while (idx < n) : (idx += 1) { | ||
d[idx] = s[idx]; | ||
if (d[idx] == needle) return d + idx + 1; | ||
} | ||
Comment on lines
+166
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks like it could be implemented better using already existing |
||
return null; | ||
} | ||
|
||
test memccpy { | ||
const src: []const u8 = "supercalifragilisticexpialidocious"; | ||
var dst: [src.len]u8 = @splat(0); | ||
const dOffset = std.mem.indexOfScalar(u8, src, 'd').?; | ||
const endPtr: *anyopaque = @ptrCast(@as([*]u8, &dst) + dOffset + 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to use |
||
try std.testing.expect(memccpy(@ptrCast(&dst), @ptrCast(src.ptr), 'd', src.len) == endPtr); | ||
try std.testing.expectEqualStrings("supercalifragilisticexpialid", std.mem.trimRight(u8, dst[0..], &.{0})); | ||
|
||
dst = @splat(0); | ||
try std.testing.expect(memccpy(@constCast(@ptrCast(&dst)), (@ptrCast(src.ptr)), 'z', src.len) == null); | ||
try std.testing.expectEqualStrings(src, dst[0..]); | ||
} | ||
|
||
fn mempcpy(noalias dest: *anyopaque, noalias src: *const anyopaque, n: usize) callconv(.c) *anyopaque { | ||
@setRuntimeSafety(false); | ||
const d: [*]u8 = @ptrCast(dest); | ||
const s: [*]const u8 = @ptrCast(src); | ||
@memcpy(d[0..n], s[0..n]); | ||
return @ptrCast(d + n); | ||
} | ||
|
||
test mempcpy { | ||
const bytesToWrite = 3; | ||
const src: []const u8 = "testing"; | ||
var dst: [src.len]u8 = @splat('z'); | ||
const endPtr: *anyopaque = @ptrCast(@as([*]u8, &dst) + bytesToWrite); | ||
try std.testing.expect(mempcpy(@ptrCast(&dst), @ptrCast(src.ptr), bytesToWrite) == endPtr); | ||
try std.testing.expect(@as(*u8, @ptrCast(endPtr)).* == 'z'); | ||
} | ||
|
||
fn memmem(haystack: *const anyopaque, haystack_len: usize, needle: *const anyopaque, needle_len: usize) callconv(.c) ?*const anyopaque { | ||
const h: [*]const c_char = @ptrCast(haystack); | ||
const n: [*]const c_char = @ptrCast(needle); | ||
|
||
if (std.mem.indexOf(c_char, h[0..haystack_len], n[0..needle_len])) |idx| { | ||
return @ptrCast(h + idx); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
fn stpcpy(noalias dest: [*:0]c_char, noalias src: [*:0]const c_char) callconv(.c) [*:0]c_char { | ||
var d: [*:0]c_char = dest; | ||
var s: [*:0]const c_char = src; | ||
// QUESTION: is std.mem.span -> @memset more efficient here? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you'd have to just measure this with a few different array sizes. |
||
while (true) { | ||
d[0] = s[0]; | ||
if (s[0] == 0) return d; | ||
d += 1; | ||
s += 1; | ||
} | ||
} | ||
|
||
test stpcpy { | ||
const src: [:0]const c_char = @ptrCast("bananas"); | ||
var dst: [src.len + 1]c_char = undefined; | ||
const endPtr: [*]c_char = @ptrCast(@as([*]c_char, &dst) + src.len); | ||
try std.testing.expect(stpcpy(@ptrCast(&dst), @ptrCast(src.ptr)) == endPtr); | ||
try std.testing.expect(endPtr[0] == 0); | ||
try std.testing.expectEqualSentinel(c_char, 0, src, dst[0..src.len :0]); | ||
} | ||
|
||
fn strcpy(noalias dest: [*:0]c_char, noalias src: [*:0]const c_char) callconv(.c) [*:0]c_char { | ||
_ = stpcpy(dest, src); | ||
return dest; | ||
} | ||
|
||
test strcpy { | ||
const src: [:0]const c_char = @ptrCast("bananas"); | ||
var dst: [src.len + 1]c_char = undefined; | ||
try std.testing.expect(strcpy(@ptrCast(&dst), @ptrCast(src.ptr)) == @as([*:0]c_char, @ptrCast(&dst))); | ||
try std.testing.expectEqualSentinel(c_char, 0, src, dst[0..src.len :0]); | ||
} | ||
|
||
fn stpncpy(noalias dest: [*:0]c_char, noalias src: [*:0]const c_char, n: usize) callconv(.c) [*:0]c_char { | ||
const dlen = strnlen(src, n); | ||
const end = mempcpy(@ptrCast(dest), @ptrCast(src), dlen); | ||
const remaining: [*]u8 = @ptrCast(end); | ||
@memset(remaining[0 .. n - dlen], 0); | ||
return @ptrCast(end); | ||
} | ||
|
||
test stpncpy { | ||
var buf: [5]c_char = undefined; | ||
const b: [*:0]c_char = @ptrCast(&buf); | ||
try std.testing.expect(stpncpy(@ptrCast(&buf), @ptrCast("1"), buf.len) == b + 1); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', 0, 0, 0, 0 }, buf[0..]); | ||
try std.testing.expect(stpncpy(@ptrCast(&buf), @ptrCast("12"), buf.len) == b + 2); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', 0, 0, 0 }, buf[0..]); | ||
try std.testing.expect(stpncpy(@ptrCast(&buf), @ptrCast("123"), buf.len) == b + 3); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', '3', 0, 0 }, buf[0..]); | ||
try std.testing.expect(stpncpy(@ptrCast(&buf), @ptrCast("1234"), buf.len) == b + 4); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', '3', '4', 0 }, buf[0..]); | ||
try std.testing.expect(stpncpy(@ptrCast(&buf), @ptrCast("12345"), buf.len) == b + 5); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', '3', '4', '5' }, buf[0..]); | ||
} | ||
|
||
fn strncpy(noalias dest: [*:0]c_char, noalias src: [*:0]const c_char, n: usize) callconv(.c) [*:0]c_char { | ||
_ = stpncpy(dest, src, n); | ||
return dest; | ||
} | ||
|
||
test strncpy { | ||
var buf: [5]c_char = undefined; | ||
const b: [*:0]c_char = @ptrCast(&buf); | ||
try std.testing.expect(strncpy(@ptrCast(&buf), @ptrCast("1"), buf.len) == b); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', 0, 0, 0, 0 }, buf[0..]); | ||
try std.testing.expect(strncpy(@ptrCast(&buf), @ptrCast("12"), buf.len) == b); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', 0, 0, 0 }, buf[0..]); | ||
try std.testing.expect(strncpy(@ptrCast(&buf), @ptrCast("123"), buf.len) == b); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', '3', 0, 0 }, buf[0..]); | ||
try std.testing.expect(strncpy(@ptrCast(&buf), @ptrCast("1234"), buf.len) == b); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', '3', '4', 0 }, buf[0..]); | ||
try std.testing.expect(strncpy(@ptrCast(&buf), @ptrCast("12345"), buf.len) == b); | ||
try std.testing.expectEqualSlices(c_char, &.{ '1', '2', '3', '4', '5' }, buf[0..]); | ||
} | ||
|
||
fn strcat(noalias dest: [*:0]c_char, noalias src: [*:0]const c_char) callconv(.c) [*:0]c_char { | ||
const start = dest + strlen(dest); | ||
_ = stpcpy(start, src); | ||
return dest; | ||
} | ||
|
||
test strcat { | ||
const start = "Hello"; | ||
const end = " World!\n"; | ||
var buf: [start.len + end.len:0]c_char = undefined; | ||
@memcpy(buf[0 .. start.len + 1], @as([*:0]const c_char, @ptrCast(start))); | ||
try std.testing.expect(strcat(&buf, @as([*:0]const c_char, @ptrCast(end))) == &buf); | ||
try std.testing.expectEqualStrings("Hello World!\n", @as([]u8, @ptrCast(buf[0..]))); | ||
} |
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks unused? We also have this in compiler-rt anyway.