Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ comptime {
_ = @import("c/inttypes.zig");
_ = @import("c/stdlib.zig");
_ = @import("c/math.zig");
_ = @import("c/string.zig");
_ = @import("c/strings.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()) {
Expand Down
1 change: 1 addition & 0 deletions lib/c/math.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const builtin = @import("builtin");

comptime {
if (builtin.target.isMinGW()) {
// Files specific to MinGW-w64.
@export(&isnan, .{ .name = "isnan", .linkage = common.linkage, .visibility = common.visibility });
@export(&isnan, .{ .name = "__isnan", .linkage = common.linkage, .visibility = common.visibility });
@export(&isnanf, .{ .name = "isnanf", .linkage = common.linkage, .visibility = common.visibility });
Expand Down
203 changes: 164 additions & 39 deletions lib/c/string.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,44 @@ 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 });
if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
// Functions specific to musl and wasi-libc.
@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 });
@export(&strspn, .{ .name = "strspn", .linkage = common.linkage, .visibility = common.visibility });
@export(&strcspn, .{ .name = "strcspn", .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 });
@export(&strtok_r, .{ .name = "strtok_r", .linkage = common.linkage, .visibility = common.visibility });
Comment on lines +7 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think all these functions are specific to musl and wasi-libc.

I'm not against special casing those ABIs sometimes but I'm pretty sure most functions should more simply be directly exposed independently of target.

Is this conditional compilation solving a problem?

}

if (builtin.target.isMinGW()) {
// Files specific to MinGW-w64.
@export(&strtok_r, .{ .name = "strtok_r", .linkage = common.linkage, .visibility = common.visibility });
}
}

fn strcmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int {
fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) c_int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous version uses c_char quite intentionally, since it has ABI implications. While pointer types have the same ABI independently of element type, this could have an effect on generated header files for example. Such a change should not be unadorned with an explanation.

// We need to perform unsigned comparisons.
return switch (std.mem.orderZ(u8, @ptrCast(s1), @ptrCast(s2))) {
return switch (std.mem.orderZ(u8, s1, s2)) {
.lt => -1,
.eq => 0,
.gt => 1,
};
}

fn strncmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c) c_int {
fn strncmp(s1: [*:0]const u8, s2: [*:0]const u8, n: usize) callconv(.c) c_int {
if (n == 0) return 0;

var l: [*:0]const u8 = @ptrCast(s1);
var r: [*:0]const u8 = @ptrCast(s2);
var l = s1;
var r = s2;
var i = n - 1;

while (l[0] != 0 and r[0] != 0 and i != 0 and l[0] == r[0]) {
Expand All @@ -38,10 +52,10 @@ fn strncmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c)
return @as(c_int, l[0]) - @as(c_int, r[0]);
}

fn strcasecmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int {
fn strcasecmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) c_int {
const toLower = std.ascii.toLower;
var l: [*:0]const u8 = @ptrCast(s1);
var r: [*:0]const u8 = @ptrCast(s2);
var l = s1;
var r = s2;

while (l[0] != 0 and r[0] != 0 and (l[0] == r[0] or toLower(l[0]) == toLower(r[0]))) {
l += 1;
Expand All @@ -51,15 +65,15 @@ fn strcasecmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int {
return @as(c_int, toLower(l[0])) - @as(c_int, toLower(r[0]));
}

fn __strcasecmp_l(s1: [*:0]const c_char, s2: [*:0]const c_char, locale: *anyopaque) callconv(.c) c_int {
fn __strcasecmp_l(s1: [*:0]const u8, s2: [*:0]const u8, locale: *anyopaque) callconv(.c) c_int {
_ = locale;
return strcasecmp(s1, s2);
}

fn strncasecmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c) c_int {
fn strncasecmp(s1: [*:0]const u8, s2: [*:0]const u8, 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 l = s1;
var r = s2;
var i = n - 1;

while (l[0] != 0 and r[0] != 0 and i != 0 and (l[0] == r[0] or toLower(l[0]) == toLower(r[0]))) {
Expand All @@ -71,34 +85,145 @@ fn strncasecmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(
return @as(c_int, toLower(l[0])) - @as(c_int, toLower(r[0]));
}

fn __strncasecmp_l(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize, locale: *anyopaque) callconv(.c) c_int {
fn __strncasecmp_l(s1: [*:0]const u8, s2: [*:0]const u8, 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);
try std.testing.expect(strcasecmp("a", "b") < 0);
try std.testing.expect(strcasecmp("b", "a") > 0);
try std.testing.expect(strcasecmp("A", "b") < 0);
try std.testing.expect(strcasecmp("b", "A") > 0);
try std.testing.expect(strcasecmp("A", "A") == 0);
try std.testing.expect(strcasecmp("B", "b") == 0);
try std.testing.expect(strcasecmp("bb", "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);
try std.testing.expect(strncasecmp("a", "b", 1) < 0);
try std.testing.expect(strncasecmp("b", "a", 1) > 0);
try std.testing.expect(strncasecmp("A", "b", 1) < 0);
try std.testing.expect(strncasecmp("b", "A", 1) > 0);
try std.testing.expect(strncasecmp("A", "A", 1) == 0);
try std.testing.expect(strncasecmp("B", "b", 1) == 0);
try std.testing.expect(strncasecmp("bb", "AA", 2) > 0);
}

test strncmp {
try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("b"), 1) < 0);
try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("c"), 1) < 0);
try std.testing.expect(strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0);
try std.testing.expect(strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0);
try std.testing.expect(strncmp("a", "b", 1) < 0);
try std.testing.expect(strncmp("a", "c", 1) < 0);
try std.testing.expect(strncmp("b", "a", 1) > 0);
try std.testing.expect(strncmp("\xff", "\x02", 1) > 0);
}

fn strspn(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) usize {
const slice1 = std.mem.span(s1);
const slice2 = std.mem.span(s2);
return std.mem.indexOfNone(u8, slice1, slice2) orelse slice1.len;
}

test strspn {
try std.testing.expectEqual(0, strspn("foobarbaz", ""));
try std.testing.expectEqual(0, strspn("foobarbaz", "c"));
try std.testing.expectEqual(3, strspn("foobarbaz", "fo"));
try std.testing.expectEqual(9, strspn("foobarbaz", "fobarz"));
try std.testing.expectEqual(9, strspn("foobarbaz", "abforz"));
}

fn strcspn(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) usize {
const slice1 = std.mem.span(s1);
const slice2 = std.mem.span(s2);
return std.mem.indexOfAny(u8, slice1, slice2) orelse slice1.len;
}

test strcspn {
try std.testing.expectEqual(0, strcspn("foobarbaz", "f"));
try std.testing.expectEqual(3, strcspn("foobarbaz", "rab"));
try std.testing.expectEqual(4, strcspn("foobarbaz", "ra"));
try std.testing.expectEqual(9, strcspn("foobarbaz", ""));
}

fn strpbrk(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) ?[*:0]const u8 {
const slice1 = std.mem.span(s1);
const slice2 = std.mem.span(s2);
const index = std.mem.indexOfAny(u8, slice1, slice2) orelse
return null;
return s1 + index;
}

test strpbrk {
try std.testing.expectEqualStrings("barbaz", std.mem.span(strpbrk("foobarbaz", "rab").?));
try std.testing.expectEqualStrings("arbaz", std.mem.span(strpbrk("foobarbaz", "ra").?));
try std.testing.expectEqual(null, strpbrk("foobarbaz", ""));
}

fn strstr(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) ?[*:0]const u8 {
const slice1 = std.mem.span(s1);
const slice2 = std.mem.span(s2);
const index = std.mem.indexOf(u8, slice1, slice2) orelse
return null;
return s1 + index;
}

test strstr {
try std.testing.expectEqualStrings("barbaz", std.mem.span(strstr("foobarbaz", "ba").?));
try std.testing.expectEqualStrings("foobarbaz", std.mem.span(strstr("foobarbaz", "fo").?));
try std.testing.expectEqualStrings("foobarbaz", std.mem.span(strstr("foobarbaz", "f").?));
try std.testing.expectEqualStrings("foobarbaz", std.mem.span(strstr("foobarbaz", "").?));
try std.testing.expectEqual(null, strstr("foobarbaz", "boofarfaz"));
try std.testing.expectEqual(null, strstr("foobarbaz", "fa"));
try std.testing.expectEqual(null, strstr("foobarbaz", "c"));
}

fn strtok_r(s: ?[*:0]u8, sep: [*:0]const u8, lasts: *?[*:0]u8) callconv(.c) ?[*:0]u8 {
const slice = std.mem.span(s orelse lasts.* orelse return null);
const delim = std.mem.span(sep);
const index = std.mem.indexOfNone(u8, slice, delim) orelse {
lasts.* = null;
return null;
};
if (std.mem.indexOfAny(u8, slice[index..], delim)) |len| {
slice[index + len] = 0;
lasts.* = slice[index + len + 1 ..];
} else {
lasts.* = null;
}
return slice[index..];
}

fn strtok(s: ?[*:0]u8, sep: [*:0]const u8) callconv(.c) ?[*:0]u8 {
const static = struct {
var lasts: ?[*:0]u8 = null;
};
return strtok_r(s, sep, &static.lasts);
}

test strtok {
var str = "?a???b,,,#c?#,".*;
{
const ret = strtok(&str, "?");
try std.testing.expectEqual(str[1..], ret);
try std.testing.expectEqualStrings("a", std.mem.span(ret.?));
}
{
const ret = strtok(null, ",");
try std.testing.expectEqual(str[3..], ret);
try std.testing.expectEqualStrings("??b", std.mem.span(ret.?));
}
{
const ret = strtok(null, "#,");
try std.testing.expectEqual(str[10..], ret);
try std.testing.expectEqualStrings("c?", std.mem.span(ret.?));
}
{
const ret = strtok(null, "?");
try std.testing.expectEqual(str[13..], ret);
try std.testing.expectEqualStrings(",", std.mem.span(ret.?));
}
{
const ret = strtok(null, "?");
try std.testing.expectEqual(null, ret);
}
try std.testing.expectEqualSlices(u8, "?a" ++ .{0} ++ "??b" ++ .{0} ++ ",,#c?" ++ .{0} ++ ",", &str);
}
6 changes: 5 additions & 1 deletion lib/c/strings.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
const builtin = @import("builtin");
const std = @import("std");
const common = @import("common.zig");

comptime {
@export(&bzero, .{ .name = "bzero", .linkage = common.linkage, .visibility = common.visibility });
if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
// Functions specific to musl and wasi-libc.
@export(&bzero, .{ .name = "bzero", .linkage = common.linkage, .visibility = common.visibility });
}
Comment on lines +6 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I don't see why this should be a conditional export.

}

fn bzero(s: *anyopaque, n: usize) callconv(.c) void {
Expand Down
98 changes: 0 additions & 98 deletions lib/libc/mingw/stdio/strtok_r.c

This file was deleted.

Loading