| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const common = @import("common.zig"); | 2 | const common = @import("common.zig"); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| | 4 | const assert = std.debug.assert; |
| 4 | const div_t = std.c.div_t; | 5 | const div_t = std.c.div_t; |
| 5 | const ldiv_t = std.c.ldiv_t; | 6 | const ldiv_t = std.c.ldiv_t; |
| 6 | const lldiv_t = std.c.lldiv_t; | 7 | const lldiv_t = std.c.lldiv_t; |
| ... | @@ -16,6 +17,24 @@ comptime { | ... | @@ -16,6 +17,24 @@ comptime { |
| 16 | @export(&ldiv, .{ .name = "ldiv", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&ldiv, .{ .name = "ldiv", .linkage = common.linkage, .visibility = common.visibility }); |
| 17 | @export(&lldiv, .{ .name = "lldiv", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&lldiv, .{ .name = "lldiv", .linkage = common.linkage, .visibility = common.visibility }); |
| 18 | | 19 | |
| | 20 | @export(&atoi, .{ .name = "atoi", .linkage = common.linkage, .visibility = common.visibility }); |
| | 21 | @export(&atol, .{ .name = "atol", .linkage = common.linkage, .visibility = common.visibility }); |
| | 22 | @export(&atoll, .{ .name = "atoll", .linkage = common.linkage, .visibility = common.visibility }); |
| | 23 | |
| | 24 | @export(&strtol, .{ .name = "strtol", .linkage = common.linkage, .visibility = common.visibility }); |
| | 25 | @export(&strtoll, .{ .name = "strtoll", .linkage = common.linkage, .visibility = common.visibility }); |
| | 26 | @export(&strtoul, .{ .name = "strtoul", .linkage = common.linkage, .visibility = common.visibility }); |
| | 27 | @export(&strtoull, .{ .name = "strtoull", .linkage = common.linkage, .visibility = common.visibility }); |
| | 28 | @export(&strtoimax, .{ .name = "strtoimax", .linkage = common.linkage, .visibility = common.visibility }); |
| | 29 | @export(&strtoumax, .{ .name = "strtoumax", .linkage = common.linkage, .visibility = common.visibility }); |
| | 30 | |
| | 31 | @export(&strtol, .{ .name = "__strtol_internal", .linkage = common.linkage, .visibility = common.visibility }); |
| | 32 | @export(&strtoll, .{ .name = "__strtoll_internal", .linkage = common.linkage, .visibility = common.visibility }); |
| | 33 | @export(&strtoul, .{ .name = "__strtoul_internal", .linkage = common.linkage, .visibility = common.visibility }); |
| | 34 | @export(&strtoull, .{ .name = "__strtoull_internal", .linkage = common.linkage, .visibility = common.visibility }); |
| | 35 | @export(&strtoimax, .{ .name = "__strtoimax_internal", .linkage = common.linkage, .visibility = common.visibility }); |
| | 36 | @export(&strtoumax, .{ .name = "__strtoumax_internal", .linkage = common.linkage, .visibility = common.visibility }); |
| | 37 | |
| 19 | @export(&qsort_r, .{ .name = "qsort_r", .linkage = common.linkage, .visibility = common.visibility }); | 38 | @export(&qsort_r, .{ .name = "qsort_r", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&qsort, .{ .name = "qsort", .linkage = common.linkage, .visibility = common.visibility }); | 39 | @export(&qsort, .{ .name = "qsort", .linkage = common.linkage, .visibility = common.visibility }); |
| 21 | | 40 | |
| ... | @@ -56,6 +75,160 @@ fn lldiv(a: c_longlong, b: c_longlong) callconv(.c) lldiv_t { | ... | @@ -56,6 +75,160 @@ fn lldiv(a: c_longlong, b: c_longlong) callconv(.c) lldiv_t { |
| 56 | }; | 75 | }; |
| 57 | } | 76 | } |
| 58 | | 77 | |
| | 78 | fn atoi(str: [*:0]const c_char) callconv(.c) c_int { |
| | 79 | return asciiToInteger(c_int, @ptrCast(str)); |
| | 80 | } |
| | 81 | |
| | 82 | fn atol(str: [*:0]const c_char) callconv(.c) c_long { |
| | 83 | return asciiToInteger(c_long, @ptrCast(str)); |
| | 84 | } |
| | 85 | |
| | 86 | fn atoll(str: [*:0]const c_char) callconv(.c) c_longlong { |
| | 87 | return asciiToInteger(c_longlong, @ptrCast(str)); |
| | 88 | } |
| | 89 | |
| | 90 | fn asciiToInteger(comptime T: type, buf: [*:0]const u8) T { |
| | 91 | comptime assert(std.math.isPowerOfTwo(@bitSizeOf(T))); |
| | 92 | |
| | 93 | var current = buf; |
| | 94 | while (std.ascii.isWhitespace(current[0])) : (current += 1) {} |
| | 95 | |
| | 96 | // The behaviour *is* undefined if the result cannot be represented |
| | 97 | // but as they are usually called with untrusted input we can just handle overflow gracefully. |
| | 98 | if (current[0] == '-') return parseDigitsWithSignGenericCharacter(T, u8, current + 1, null, 10, .neg) catch std.math.minInt(T); |
| | 99 | if (current[0] == '+') current += 1; |
| | 100 | return parseDigitsWithSignGenericCharacter(T, u8, current, null, 10, .pos) catch std.math.maxInt(T); |
| | 101 | } |
| | 102 | |
| | 103 | fn strtol(noalias str: [*:0]const c_char, noalias str_end: ?*[*:0]const c_char, base: c_int) callconv(.c) c_long { |
| | 104 | return stringToInteger(c_long, @ptrCast(str), if (str_end) |end| @ptrCast(end) else null, base); |
| | 105 | } |
| | 106 | |
| | 107 | fn strtoll(noalias str: [*:0]const c_char, noalias str_end: ?*[*:0]const c_char, base: c_int) callconv(.c) c_longlong { |
| | 108 | return stringToInteger(c_longlong, @ptrCast(str), if (str_end) |end| @ptrCast(end) else null, base); |
| | 109 | } |
| | 110 | |
| | 111 | fn strtoul(noalias str: [*:0]const c_char, noalias str_end: ?*[*:0]const c_char, base: c_int) callconv(.c) c_ulong { |
| | 112 | return stringToInteger(c_ulong, @ptrCast(str), if (str_end) |end| @ptrCast(end) else null, base); |
| | 113 | } |
| | 114 | |
| | 115 | fn strtoull(noalias str: [*:0]const c_char, noalias str_end: ?*[*:0]const c_char, base: c_int) callconv(.c) c_ulonglong { |
| | 116 | return stringToInteger(c_ulonglong, @ptrCast(str), if (str_end) |end| @ptrCast(end) else null, base); |
| | 117 | } |
| | 118 | |
| | 119 | // XXX: These belong in inttypes.zig but we'd have to make stringToInteger pub or move it somewhere else. |
| | 120 | fn strtoimax(noalias str: [*:0]const c_char, noalias str_end: ?*[*:0]const c_char, base: c_int) callconv(.c) std.c.intmax_t { |
| | 121 | return stringToInteger(std.c.intmax_t, @ptrCast(str), if (str_end) |end| @ptrCast(end) else null, base); |
| | 122 | } |
| | 123 | |
| | 124 | fn strtoumax(noalias str: [*:0]const c_char, noalias str_end: ?*[*:0]const c_char, base: c_int) callconv(.c) std.c.uintmax_t { |
| | 125 | return stringToInteger(std.c.uintmax_t, @ptrCast(str), if (str_end) |end| @ptrCast(end) else null, base); |
| | 126 | } |
| | 127 | |
| | 128 | fn stringToInteger(comptime T: type, noalias buf: [*:0]const u8, noalias maybe_end: ?*[*:0]const u8, base: c_int) T { |
| | 129 | comptime assert(std.math.isPowerOfTwo(@bitSizeOf(T))); |
| | 130 | |
| | 131 | if (base == 1 or base > 36) { |
| | 132 | if (maybe_end) |end| { |
| | 133 | end.* = buf; |
| | 134 | } |
| | 135 | |
| | 136 | std.c._errno().* = @intFromEnum(std.c.E.INVAL); |
| | 137 | return 0; |
| | 138 | } |
| | 139 | |
| | 140 | var current = buf; |
| | 141 | while (std.ascii.isWhitespace(current[0])) : (current += 1) {} |
| | 142 | |
| | 143 | const negative: bool = switch (current[0]) { |
| | 144 | '-' => blk: { |
| | 145 | current += 1; |
| | 146 | break :blk true; |
| | 147 | }, |
| | 148 | '+' => blk: { |
| | 149 | current += 1; |
| | 150 | break :blk false; |
| | 151 | }, |
| | 152 | else => false, |
| | 153 | }; |
| | 154 | |
| | 155 | // The prefix is allowed iff base == 0 or base == base of the prefix |
| | 156 | const real_base: u6 = if (current[0] == '0') blk: { |
| | 157 | current += 1; |
| | 158 | |
| | 159 | if ((base == 0 or base == 16) and std.ascii.toLower(current[0]) == 'x' and std.ascii.isHex(current[1])) { |
| | 160 | current += 1; |
| | 161 | break :blk 16; |
| | 162 | } |
| | 163 | |
| | 164 | if ((base == 0 or base == 8) and std.ascii.isDigit(current[0])) { |
| | 165 | break :blk 8; |
| | 166 | } |
| | 167 | |
| | 168 | break :blk switch (base) { |
| | 169 | 0 => 10, |
| | 170 | else => @intCast(base), |
| | 171 | }; |
| | 172 | } else switch (base) { |
| | 173 | 0 => 10, |
| | 174 | else => @intCast(base), |
| | 175 | }; |
| | 176 | |
| | 177 | if (@typeInfo(T).int.signedness == .unsigned) { |
| | 178 | const result = parseDigitsWithSignGenericCharacter(T, u8, current, maybe_end, real_base, .pos) catch { |
| | 179 | std.c._errno().* = @intFromEnum(std.c.E.RANGE); |
| | 180 | return std.math.maxInt(T); |
| | 181 | }; |
| | 182 | |
| | 183 | return if (negative) -%result else result; |
| | 184 | } |
| | 185 | |
| | 186 | if (negative) return parseDigitsWithSignGenericCharacter(T, u8, current, maybe_end, real_base, .neg) catch blk: { |
| | 187 | std.c._errno().* = @intFromEnum(std.c.E.RANGE); |
| | 188 | break :blk std.math.minInt(T); |
| | 189 | }; |
| | 190 | |
| | 191 | return parseDigitsWithSignGenericCharacter(T, u8, current, maybe_end, real_base, .pos) catch blk: { |
| | 192 | std.c._errno().* = @intFromEnum(std.c.E.RANGE); |
| | 193 | break :blk std.math.maxInt(T); |
| | 194 | }; |
| | 195 | } |
| | 196 | |
| | 197 | fn parseDigitsWithSignGenericCharacter( |
| | 198 | comptime T: type, |
| | 199 | comptime Char: type, |
| | 200 | noalias buf: [*:0]const Char, |
| | 201 | noalias maybe_end: ?*[*:0]const Char, |
| | 202 | base: u6, |
| | 203 | comptime sign: enum { pos, neg }, |
| | 204 | ) error{Overflow}!T { |
| | 205 | assert(base >= 2 and base <= 36); |
| | 206 | |
| | 207 | var current = buf; |
| | 208 | defer if (maybe_end) |end| { |
| | 209 | end.* = current; |
| | 210 | }; |
| | 211 | |
| | 212 | const add = switch (sign) { |
| | 213 | .pos => std.math.add, |
| | 214 | .neg => std.math.sub, |
| | 215 | }; |
| | 216 | |
| | 217 | var value: T = 0; |
| | 218 | while (true) { |
| | 219 | const c: u8 = std.math.cast(u8, current[0]) orelse break; |
| | 220 | if (!std.ascii.isAlphanumeric(c)) break; |
| | 221 | |
| | 222 | const digit: u6 = @intCast(std.fmt.charToDigit(c, base) catch break); |
| | 223 | defer current += 1; |
| | 224 | |
| | 225 | value = try std.math.mul(T, value, base); |
| | 226 | value = try add(T, value, digit); |
| | 227 | } |
| | 228 | |
| | 229 | return value; |
| | 230 | } |
| | 231 | |
| 59 | // NOTE: Despite its name, `qsort` doesn't have to use quicksort or make any complexity or stability guarantee. | 232 | // NOTE: Despite its name, `qsort` doesn't have to use quicksort or make any complexity or stability guarantee. |
| 60 | fn qsort_r(base: *anyopaque, n: usize, size: usize, compare: *const fn (a: *const anyopaque, b: *const anyopaque, arg: ?*anyopaque) callconv(.c) c_int, arg: ?*anyopaque) callconv(.c) void { | 233 | fn qsort_r(base: *anyopaque, n: usize, size: usize, compare: *const fn (a: *const anyopaque, b: *const anyopaque, arg: ?*anyopaque) callconv(.c) c_int, arg: ?*anyopaque) callconv(.c) void { |
| 61 | const Context = struct { | 234 | const Context = struct { |
| ... | @@ -147,6 +320,53 @@ test lldiv { | ... | @@ -147,6 +320,53 @@ test lldiv { |
| 147 | try std.testing.expectEqual(expected, lldiv(5, 3)); | 320 | try std.testing.expectEqual(expected, lldiv(5, 3)); |
| 148 | } | 321 | } |
| 149 | | 322 | |
| | 323 | test atoi { |
| | 324 | try std.testing.expectEqual(0, atoi(@ptrCast("stop42true"))); |
| | 325 | try std.testing.expectEqual(42, atoi(@ptrCast("42true"))); |
| | 326 | try std.testing.expectEqual(-1, atoi(@ptrCast("-01"))); |
| | 327 | try std.testing.expectEqual(1, atoi(@ptrCast("+001"))); |
| | 328 | try std.testing.expectEqual(100, atoi(@ptrCast(" 100"))); |
| | 329 | try std.testing.expectEqual(500, atoi(@ptrCast("000000000000500"))); |
| | 330 | try std.testing.expectEqual(1111, atoi(@ptrCast("0000000000001111_0000"))); |
| | 331 | try std.testing.expectEqual(0, atoi(@ptrCast("0xAA"))); |
| | 332 | try std.testing.expectEqual(700, atoi(@ptrCast("700B"))); |
| | 333 | try std.testing.expectEqual(32453, atoi(@ptrCast("+32453more"))); |
| | 334 | try std.testing.expectEqual(std.math.maxInt(c_int), atoi(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.maxInt(c_int)})))); |
| | 335 | try std.testing.expectEqual(std.math.minInt(c_int), atoi(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.minInt(c_int)})))); |
| | 336 | } |
| | 337 | |
| | 338 | test atol { |
| | 339 | try std.testing.expectEqual(0, atol(@ptrCast("stop42true"))); |
| | 340 | try std.testing.expectEqual(42, atol(@ptrCast("42true"))); |
| | 341 | try std.testing.expectEqual(-1, atol(@ptrCast("-01"))); |
| | 342 | try std.testing.expectEqual(1, atol(@ptrCast("+001"))); |
| | 343 | try std.testing.expectEqual(100, atol(@ptrCast(" 100"))); |
| | 344 | try std.testing.expectEqual(500, atol(@ptrCast("000000000000500"))); |
| | 345 | try std.testing.expectEqual(1111, atol(@ptrCast("0000000000001111_0000"))); |
| | 346 | try std.testing.expectEqual(0, atol(@ptrCast("0xAA"))); |
| | 347 | try std.testing.expectEqual(700, atol(@ptrCast("700B"))); |
| | 348 | try std.testing.expectEqual(32453, atol(@ptrCast("+32453more"))); |
| | 349 | try std.testing.expectEqual(std.math.maxInt(c_long), atol(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.maxInt(c_long)})))); |
| | 350 | try std.testing.expectEqual(std.math.minInt(c_long), atol(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.minInt(c_long)})))); |
| | 351 | } |
| | 352 | |
| | 353 | test atoll { |
| | 354 | try std.testing.expectEqual(0, atoll(@ptrCast("stop42true"))); |
| | 355 | try std.testing.expectEqual(42, atoll(@ptrCast("42true"))); |
| | 356 | try std.testing.expectEqual(-1, atoll(@ptrCast("-01"))); |
| | 357 | try std.testing.expectEqual(1, atoll(@ptrCast("+001"))); |
| | 358 | try std.testing.expectEqual(100, atoll(@ptrCast(" 100"))); |
| | 359 | try std.testing.expectEqual(500, atoll(@ptrCast("000000000000500"))); |
| | 360 | try std.testing.expectEqual(1111, atoll(@ptrCast("0000000000001111_0000"))); |
| | 361 | try std.testing.expectEqual(0, atoll(@ptrCast("0xAA"))); |
| | 362 | try std.testing.expectEqual(700, atoll(@ptrCast("700B"))); |
| | 363 | try std.testing.expectEqual(32453, atoll(@ptrCast(" +32453more"))); |
| | 364 | try std.testing.expectEqual(std.math.maxInt(c_longlong), atoll(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.maxInt(c_longlong)})))); |
| | 365 | try std.testing.expectEqual(std.math.minInt(c_longlong), atoll(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.minInt(c_longlong)})))); |
| | 366 | } |
| | 367 | |
| | 368 | // FIXME: We cannot test strtol, strtoll, strtoul, etc.. here as it must modify errno and libc is not linked in tests |
| | 369 | |
| 150 | test bsearch { | 370 | test bsearch { |
| 151 | const Comparison = struct { | 371 | const Comparison = struct { |
| 152 | pub fn compare(a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int { | 372 | pub fn compare(a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int { |