mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Switch builtins to use roc_alloc and friends
This commit is contained in:
parent
8cafabc0c9
commit
ab51582541
4 changed files with 299 additions and 297 deletions
|
@ -1,9 +1,9 @@
|
|||
const utils = @import("utils.zig");
|
||||
const roc_mem = @import("mem.zig");
|
||||
const RocList = @import("list.zig").RocList;
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||
const Allocator = mem.Allocator;
|
||||
const unicode = std.unicode;
|
||||
const testing = std.testing;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
@ -34,6 +34,8 @@ pub const RocStr = extern struct {
|
|||
str_bytes: ?[*]u8,
|
||||
str_len: usize,
|
||||
|
||||
pub const alignment = @alignOf(usize);
|
||||
|
||||
pub inline fn empty() RocStr {
|
||||
return RocStr{
|
||||
.str_len = 0,
|
||||
|
@ -43,15 +45,15 @@ pub const RocStr = extern struct {
|
|||
|
||||
// This clones the pointed-to bytes if they won't fit in a
|
||||
// small string, and returns a (pointer, len) tuple which points to them.
|
||||
pub fn init(allocator: *Allocator, bytes_ptr: [*]const u8, length: usize) RocStr {
|
||||
var result = RocStr.allocate(allocator, InPlace.Clone, length);
|
||||
pub fn init(bytes_ptr: [*]const u8, length: usize) RocStr {
|
||||
var result = RocStr.allocate(InPlace.Clone, length);
|
||||
@memcpy(result.asU8ptr(), bytes_ptr, length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn initBig(allocator: *Allocator, in_place: InPlace, number_of_chars: u64) RocStr {
|
||||
const first_element = utils.allocateWithRefcount(allocator, @sizeOf(usize), number_of_chars);
|
||||
pub fn initBig(in_place: InPlace, number_of_chars: u64) RocStr {
|
||||
const first_element = utils.allocateWithRefcount(@sizeOf(usize), number_of_chars);
|
||||
|
||||
return RocStr{
|
||||
.str_bytes = first_element,
|
||||
|
@ -60,11 +62,11 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
// allocate space for a (big or small) RocStr, but put nothing in it yet
|
||||
pub fn allocate(allocator: *Allocator, result_in_place: InPlace, number_of_chars: usize) RocStr {
|
||||
pub fn allocate(result_in_place: InPlace, number_of_chars: usize) RocStr {
|
||||
const result_is_big = number_of_chars >= small_string_size;
|
||||
|
||||
if (result_is_big) {
|
||||
return RocStr.initBig(allocator, result_in_place, number_of_chars);
|
||||
return RocStr.initBig(result_in_place, number_of_chars);
|
||||
} else {
|
||||
var t = blank_small_string;
|
||||
|
||||
|
@ -77,10 +79,9 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: RocStr, allocator: *Allocator) void {
|
||||
pub fn deinit(self: RocStr) void {
|
||||
if (!self.isSmallStr() and !self.isEmpty()) {
|
||||
const alignment = @alignOf(usize);
|
||||
utils.decref(allocator, alignment, self.str_bytes, self.str_len);
|
||||
utils.decref(RocStr.alignment, self.str_bytes, self.str_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +99,7 @@ pub const RocStr = extern struct {
|
|||
if (length < roc_str_size) {
|
||||
return RocStr.empty();
|
||||
} else {
|
||||
var new_bytes: []T = allocator.alloc(u8, length) catch unreachable;
|
||||
var new_bytes: []T = utils.alloc(RocStr.alignment, length) catch unreachable;
|
||||
|
||||
var new_bytes_ptr: [*]u8 = @ptrCast([*]u8, &new_bytes);
|
||||
|
||||
|
@ -146,12 +147,12 @@ pub const RocStr = extern struct {
|
|||
return true;
|
||||
}
|
||||
|
||||
pub fn clone(allocator: *Allocator, in_place: InPlace, str: RocStr) RocStr {
|
||||
pub fn clone(in_place: InPlace, str: RocStr) RocStr {
|
||||
if (str.isSmallStr() or str.isEmpty()) {
|
||||
// just return the bytes
|
||||
return str;
|
||||
} else {
|
||||
var new_str = RocStr.initBig(allocator, in_place, str.str_len);
|
||||
var new_str = RocStr.initBig(in_place, str.str_len);
|
||||
|
||||
var old_bytes: [*]u8 = @ptrCast([*]u8, str.str_bytes);
|
||||
var new_bytes: [*]u8 = @ptrCast([*]u8, new_str.str_bytes);
|
||||
|
@ -164,33 +165,30 @@ pub const RocStr = extern struct {
|
|||
|
||||
pub fn reallocate(
|
||||
self: RocStr,
|
||||
allocator: *Allocator,
|
||||
new_length: usize,
|
||||
) RocStr {
|
||||
const alignment = 1;
|
||||
const element_width = 1;
|
||||
|
||||
if (self.bytes) |source_ptr| {
|
||||
if (self.isUnique()) {
|
||||
const new_source = utils.unsafeReallocate(source_ptr, allocator, alignment, self.len(), new_length, element_width);
|
||||
const new_source = utils.unsafeReallocate(source_ptr, RocStr.alignment, self.len(), new_length, element_width);
|
||||
|
||||
return RocStr{ .str_bytes = new_source, .str_len = new_length };
|
||||
}
|
||||
}
|
||||
|
||||
return self.reallocateFresh(allocator, alignment, new_length, element_width);
|
||||
return self.reallocateFresh(RocStr.alignment, new_length, element_width);
|
||||
}
|
||||
|
||||
/// reallocate by explicitly making a new allocation and copying elements over
|
||||
pub fn reallocateFresh(
|
||||
self: RocStr,
|
||||
allocator: *Allocator,
|
||||
new_length: usize,
|
||||
) RocStr {
|
||||
const old_length = self.len();
|
||||
const delta_length = new_length - old_length;
|
||||
|
||||
const result = RocStr.allocate(allocator, InPlace.Clone, new_length);
|
||||
const result = RocStr.allocate(InPlace.Clone, new_length);
|
||||
|
||||
// transfer the memory
|
||||
|
||||
|
@ -200,7 +198,7 @@ pub const RocStr = extern struct {
|
|||
@memcpy(dest_ptr, source_ptr, old_length);
|
||||
@memset(dest_ptr + old_length, 0, delta_length);
|
||||
|
||||
self.deinit(allocator);
|
||||
self.deinit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -268,33 +266,33 @@ pub const RocStr = extern struct {
|
|||
const str1_len = 3;
|
||||
var str1: [str1_len]u8 = "abc".*;
|
||||
const str1_ptr: [*]u8 = &str1;
|
||||
var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
|
||||
var roc_str1 = RocStr.init(str1_ptr, str1_len);
|
||||
|
||||
const str2_len = 3;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
const str2_ptr: [*]u8 = &str2;
|
||||
var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
|
||||
var roc_str2 = RocStr.init(str2_ptr, str2_len);
|
||||
|
||||
expect(roc_str1.eq(roc_str2));
|
||||
|
||||
roc_str1.deinit(testing.allocator);
|
||||
roc_str2.deinit(testing.allocator);
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
}
|
||||
|
||||
test "RocStr.eq: not equal different length" {
|
||||
const str1_len = 4;
|
||||
var str1: [str1_len]u8 = "abcd".*;
|
||||
const str1_ptr: [*]u8 = &str1;
|
||||
var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
|
||||
var roc_str1 = RocStr.init(str1_ptr, str1_len);
|
||||
|
||||
const str2_len = 3;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
const str2_ptr: [*]u8 = &str2;
|
||||
var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
|
||||
var roc_str2 = RocStr.init(str2_ptr, str2_len);
|
||||
|
||||
defer {
|
||||
roc_str1.deinit(testing.allocator);
|
||||
roc_str2.deinit(testing.allocator);
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
}
|
||||
|
||||
expect(!roc_str1.eq(roc_str2));
|
||||
|
@ -304,16 +302,16 @@ pub const RocStr = extern struct {
|
|||
const str1_len = 3;
|
||||
var str1: [str1_len]u8 = "acb".*;
|
||||
const str1_ptr: [*]u8 = &str1;
|
||||
var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
|
||||
var roc_str1 = RocStr.init(str1_ptr, str1_len);
|
||||
|
||||
const str2_len = 3;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
const str2_ptr: [*]u8 = &str2;
|
||||
var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
|
||||
var roc_str2 = RocStr.init(str2_ptr, str2_len);
|
||||
|
||||
defer {
|
||||
roc_str1.deinit(testing.allocator);
|
||||
roc_str2.deinit(testing.allocator);
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
}
|
||||
|
||||
expect(!roc_str1.eq(roc_str2));
|
||||
|
@ -321,7 +319,7 @@ pub const RocStr = extern struct {
|
|||
};
|
||||
|
||||
pub fn init(bytes_ptr: [*]const u8, length: usize) callconv(.C) RocStr {
|
||||
return @call(.{ .modifier = always_inline }, RocStr.init, .{ std.heap.c_allocator, bytes_ptr, length });
|
||||
return @call(.{ .modifier = always_inline }, RocStr.init, .{ bytes_ptr, length });
|
||||
}
|
||||
|
||||
// Str.equal
|
||||
|
@ -335,17 +333,12 @@ pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize {
|
|||
}
|
||||
|
||||
// Str.fromInt
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strFromIntC(int: i64) callconv(.C) RocStr {
|
||||
return strFromInt(std.heap.c_allocator, int);
|
||||
}
|
||||
|
||||
fn strFromInt(allocator: *Allocator, int: i64) RocStr {
|
||||
// prepare for having multiple integer types in the future
|
||||
return @call(.{ .modifier = always_inline }, strFromIntHelp, .{ allocator, i64, int });
|
||||
return @call(.{ .modifier = always_inline }, strFromIntHelp, .{ i64, int });
|
||||
}
|
||||
|
||||
fn strFromIntHelp(allocator: *Allocator, comptime T: type, int: T) RocStr {
|
||||
fn strFromIntHelp(comptime T: type, int: T) RocStr {
|
||||
// determine maximum size for this T
|
||||
comptime const size = comptime blk: {
|
||||
// the string representation of the minimum i128 value uses at most 40 characters
|
||||
|
@ -357,11 +350,10 @@ fn strFromIntHelp(allocator: *Allocator, comptime T: type, int: T) RocStr {
|
|||
var buf: [size]u8 = undefined;
|
||||
const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable;
|
||||
|
||||
return RocStr.init(allocator, &buf, result.len);
|
||||
return RocStr.init(&buf, result.len);
|
||||
}
|
||||
|
||||
// Str.fromFloat
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strFromFloatC(float: f64) callconv(.C) RocStr {
|
||||
// NOTE the compiled zig for float formatting seems to use LLVM11-specific features
|
||||
// hopefully we can use zig instead of snprintf in the future when we upgrade
|
||||
|
@ -374,16 +366,15 @@ pub fn strFromFloatC(float: f64) callconv(.C) RocStr {
|
|||
|
||||
const result = c.snprintf(&buf, 100, "%f", float);
|
||||
|
||||
return RocStr.init(std.heap.c_allocator, &buf, @intCast(usize, result));
|
||||
return RocStr.init(&buf, @intCast(usize, result));
|
||||
}
|
||||
|
||||
// Str.split
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strSplitInPlaceC(array: [*]RocStr, string: RocStr, delimiter: RocStr) callconv(.C) void {
|
||||
return @call(.{ .modifier = always_inline }, strSplitInPlace, .{ std.heap.c_allocator, array, string, delimiter });
|
||||
return @call(.{ .modifier = always_inline }, strSplitInPlace, .{ array, string, delimiter });
|
||||
}
|
||||
|
||||
fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
|
||||
fn strSplitInPlace(array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
|
||||
var ret_array_index: usize = 0;
|
||||
var slice_start_index: usize = 0;
|
||||
var str_index: usize = 0;
|
||||
|
@ -415,7 +406,7 @@ fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, deli
|
|||
if (matches_delimiter) {
|
||||
const segment_len: usize = str_index - slice_start_index;
|
||||
|
||||
array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, segment_len);
|
||||
array[ret_array_index] = RocStr.init(str_bytes + slice_start_index, segment_len);
|
||||
slice_start_index = str_index + delimiter_len;
|
||||
ret_array_index += 1;
|
||||
str_index += delimiter_len;
|
||||
|
@ -425,21 +416,21 @@ fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, deli
|
|||
}
|
||||
}
|
||||
|
||||
array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, str_len - slice_start_index);
|
||||
array[ret_array_index] = RocStr.init(str_bytes + slice_start_index, str_len - slice_start_index);
|
||||
}
|
||||
|
||||
test "strSplitInPlace: no delimiter" {
|
||||
// Str.split "abc" "!" == [ "abc" ]
|
||||
const str_arr = "abc";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "!";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
var array: [1]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
|
||||
strSplitInPlace(array_ptr, str, delimiter);
|
||||
|
||||
var expected = [1]RocStr{
|
||||
str,
|
||||
|
@ -447,15 +438,15 @@ test "strSplitInPlace: no delimiter" {
|
|||
|
||||
defer {
|
||||
for (array) |roc_str| {
|
||||
roc_str.deinit(testing.allocator);
|
||||
roc_str.deinit();
|
||||
}
|
||||
|
||||
for (expected) |roc_str| {
|
||||
roc_str.deinit(testing.allocator);
|
||||
roc_str.deinit();
|
||||
}
|
||||
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
expectEqual(array.len, expected.len);
|
||||
|
@ -464,10 +455,10 @@ test "strSplitInPlace: no delimiter" {
|
|||
|
||||
test "strSplitInPlace: empty end" {
|
||||
const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "---- ---- ---- ---- ----";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 3;
|
||||
var array: [array_len]RocStr = [_]RocStr{
|
||||
|
@ -477,10 +468,10 @@ test "strSplitInPlace: empty end" {
|
|||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
|
||||
strSplitInPlace(array_ptr, str, delimiter);
|
||||
|
||||
const one = RocStr.init(testing.allocator, "1", 1);
|
||||
const two = RocStr.init(testing.allocator, "2", 1);
|
||||
const one = RocStr.init("1", 1);
|
||||
const two = RocStr.init("2", 1);
|
||||
|
||||
var expected = [3]RocStr{
|
||||
one, two, RocStr.empty(),
|
||||
|
@ -488,15 +479,15 @@ test "strSplitInPlace: empty end" {
|
|||
|
||||
defer {
|
||||
for (array) |rocStr| {
|
||||
rocStr.deinit(testing.allocator);
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
for (expected) |rocStr| {
|
||||
rocStr.deinit(testing.allocator);
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
expectEqual(array.len, expected.len);
|
||||
|
@ -507,10 +498,10 @@ test "strSplitInPlace: empty end" {
|
|||
|
||||
test "strSplitInPlace: delimiter on sides" {
|
||||
const str_arr = "tttghittt";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "ttt";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 3;
|
||||
var array: [array_len]RocStr = [_]RocStr{
|
||||
|
@ -519,10 +510,10 @@ test "strSplitInPlace: delimiter on sides" {
|
|||
undefined,
|
||||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
|
||||
strSplitInPlace(array_ptr, str, delimiter);
|
||||
|
||||
const ghi_arr = "ghi";
|
||||
const ghi = RocStr.init(testing.allocator, ghi_arr, ghi_arr.len);
|
||||
const ghi = RocStr.init(ghi_arr, ghi_arr.len);
|
||||
|
||||
var expected = [3]RocStr{
|
||||
RocStr.empty(), ghi, RocStr.empty(),
|
||||
|
@ -530,15 +521,15 @@ test "strSplitInPlace: delimiter on sides" {
|
|||
|
||||
defer {
|
||||
for (array) |rocStr| {
|
||||
rocStr.deinit(testing.allocator);
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
for (expected) |rocStr| {
|
||||
rocStr.deinit(testing.allocator);
|
||||
rocStr.deinit();
|
||||
}
|
||||
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
expectEqual(array.len, expected.len);
|
||||
|
@ -550,20 +541,20 @@ test "strSplitInPlace: delimiter on sides" {
|
|||
test "strSplitInPlace: three pieces" {
|
||||
// Str.split "a!b!c" "!" == [ "a", "b", "c" ]
|
||||
const str_arr = "a!b!c";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "!";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 3;
|
||||
var array: [array_len]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
|
||||
strSplitInPlace(array_ptr, str, delimiter);
|
||||
|
||||
const a = RocStr.init(testing.allocator, "a", 1);
|
||||
const b = RocStr.init(testing.allocator, "b", 1);
|
||||
const c = RocStr.init(testing.allocator, "c", 1);
|
||||
const a = RocStr.init("a", 1);
|
||||
const b = RocStr.init("b", 1);
|
||||
const c = RocStr.init("c", 1);
|
||||
|
||||
var expected_array = [array_len]RocStr{
|
||||
a, b, c,
|
||||
|
@ -571,15 +562,15 @@ test "strSplitInPlace: three pieces" {
|
|||
|
||||
defer {
|
||||
for (array) |roc_str| {
|
||||
roc_str.deinit(testing.allocator);
|
||||
roc_str.deinit();
|
||||
}
|
||||
|
||||
for (expected_array) |roc_str| {
|
||||
roc_str.deinit(testing.allocator);
|
||||
roc_str.deinit();
|
||||
}
|
||||
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
expectEqual(expected_array.len, array.len);
|
||||
|
@ -637,14 +628,14 @@ test "countSegments: long delimiter" {
|
|||
// Str.split "str" "delimiter" == [ "str" ]
|
||||
// 1 segment
|
||||
const str_arr = "str";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "delimiter";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
defer {
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
const segments_count = countSegments(str, delimiter);
|
||||
|
@ -655,14 +646,14 @@ test "countSegments: delimiter at start" {
|
|||
// Str.split "hello there" "hello" == [ "", " there" ]
|
||||
// 2 segments
|
||||
const str_arr = "hello there";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "hello";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
defer {
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
const segments_count = countSegments(str, delimiter);
|
||||
|
@ -674,14 +665,14 @@ test "countSegments: delimiter interspered" {
|
|||
// Str.split "a!b!c" "!" == [ "a", "b", "c" ]
|
||||
// 3 segments
|
||||
const str_arr = "a!b!c";
|
||||
const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
const delimiter_arr = "!";
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
|
||||
defer {
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
str.deinit();
|
||||
delimiter.deinit();
|
||||
}
|
||||
|
||||
const segments_count = countSegments(str, delimiter);
|
||||
|
@ -736,8 +727,8 @@ test "countGraphemeClusters: empty string" {
|
|||
test "countGraphemeClusters: ascii characters" {
|
||||
const bytes_arr = "abcd";
|
||||
const bytes_len = bytes_arr.len;
|
||||
const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init(bytes_arr, bytes_len);
|
||||
defer str.deinit();
|
||||
|
||||
const count = countGraphemeClusters(str);
|
||||
expectEqual(count, 4);
|
||||
|
@ -746,8 +737,8 @@ test "countGraphemeClusters: ascii characters" {
|
|||
test "countGraphemeClusters: utf8 characters" {
|
||||
const bytes_arr = "ãxā";
|
||||
const bytes_len = bytes_arr.len;
|
||||
const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init(bytes_arr, bytes_len);
|
||||
defer str.deinit();
|
||||
|
||||
const count = countGraphemeClusters(str);
|
||||
expectEqual(count, 3);
|
||||
|
@ -756,8 +747,8 @@ test "countGraphemeClusters: utf8 characters" {
|
|||
test "countGraphemeClusters: emojis" {
|
||||
const bytes_arr = "🤔🤔🤔";
|
||||
const bytes_len = bytes_arr.len;
|
||||
const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init(bytes_arr, bytes_len);
|
||||
defer str.deinit();
|
||||
|
||||
const count = countGraphemeClusters(str);
|
||||
expectEqual(count, 3);
|
||||
|
@ -766,8 +757,8 @@ test "countGraphemeClusters: emojis" {
|
|||
test "countGraphemeClusters: emojis and ut8 characters" {
|
||||
const bytes_arr = "🤔å🤔¥🤔ç";
|
||||
const bytes_len = bytes_arr.len;
|
||||
const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init(bytes_arr, bytes_len);
|
||||
defer str.deinit();
|
||||
|
||||
const count = countGraphemeClusters(str);
|
||||
expectEqual(count, 6);
|
||||
|
@ -776,8 +767,8 @@ test "countGraphemeClusters: emojis and ut8 characters" {
|
|||
test "countGraphemeClusters: emojis, ut8, and ascii characters" {
|
||||
const bytes_arr = "6🤔å🤔e¥🤔çpp";
|
||||
const bytes_len = bytes_arr.len;
|
||||
const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init(bytes_arr, bytes_len);
|
||||
defer str.deinit();
|
||||
|
||||
const count = countGraphemeClusters(str);
|
||||
expectEqual(count, 10);
|
||||
|
@ -828,36 +819,36 @@ pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool {
|
|||
}
|
||||
|
||||
test "startsWithCodePoint: ascii char" {
|
||||
const whole = RocStr.init(testing.allocator, "foobar", 6);
|
||||
const whole = RocStr.init("foobar", 6);
|
||||
const prefix = 'f';
|
||||
expect(startsWithCodePoint(whole, prefix));
|
||||
}
|
||||
|
||||
test "startsWithCodePoint: emoji" {
|
||||
const yes = RocStr.init(testing.allocator, "💖foobar", 10);
|
||||
const no = RocStr.init(testing.allocator, "foobar", 6);
|
||||
const yes = RocStr.init("💖foobar", 10);
|
||||
const no = RocStr.init("foobar", 6);
|
||||
const prefix = '💖';
|
||||
expect(startsWithCodePoint(yes, prefix));
|
||||
expect(!startsWithCodePoint(no, prefix));
|
||||
}
|
||||
|
||||
test "startsWith: foo starts with fo" {
|
||||
const foo = RocStr.init(testing.allocator, "foo", 3);
|
||||
const fo = RocStr.init(testing.allocator, "fo", 2);
|
||||
const foo = RocStr.init("foo", 3);
|
||||
const fo = RocStr.init("fo", 2);
|
||||
expect(startsWith(foo, fo));
|
||||
}
|
||||
|
||||
test "startsWith: 123456789123456789 starts with 123456789123456789" {
|
||||
const str = RocStr.init(testing.allocator, "123456789123456789", 18);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init("123456789123456789", 18);
|
||||
defer str.deinit();
|
||||
expect(startsWith(str, str));
|
||||
}
|
||||
|
||||
test "startsWith: 12345678912345678910 starts with 123456789123456789" {
|
||||
const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
|
||||
defer str.deinit(testing.allocator);
|
||||
const prefix = RocStr.init(testing.allocator, "123456789123456789", 18);
|
||||
defer prefix.deinit(testing.allocator);
|
||||
const str = RocStr.init("12345678912345678910", 20);
|
||||
defer str.deinit();
|
||||
const prefix = RocStr.init("123456789123456789", 18);
|
||||
defer prefix.deinit();
|
||||
|
||||
expect(startsWith(str, prefix));
|
||||
}
|
||||
|
@ -886,63 +877,60 @@ pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
|
|||
}
|
||||
|
||||
test "endsWith: foo ends with oo" {
|
||||
const foo = RocStr.init(testing.allocator, "foo", 3);
|
||||
const oo = RocStr.init(testing.allocator, "oo", 2);
|
||||
defer foo.deinit(testing.allocator);
|
||||
defer oo.deinit(testing.allocator);
|
||||
const foo = RocStr.init("foo", 3);
|
||||
const oo = RocStr.init("oo", 2);
|
||||
defer foo.deinit();
|
||||
defer oo.deinit();
|
||||
|
||||
expect(endsWith(foo, oo));
|
||||
}
|
||||
|
||||
test "endsWith: 123456789123456789 ends with 123456789123456789" {
|
||||
const str = RocStr.init(testing.allocator, "123456789123456789", 18);
|
||||
defer str.deinit(testing.allocator);
|
||||
const str = RocStr.init("123456789123456789", 18);
|
||||
defer str.deinit();
|
||||
expect(endsWith(str, str));
|
||||
}
|
||||
|
||||
test "endsWith: 12345678912345678910 ends with 345678912345678910" {
|
||||
const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
|
||||
const suffix = RocStr.init(testing.allocator, "345678912345678910", 18);
|
||||
defer str.deinit(testing.allocator);
|
||||
defer suffix.deinit(testing.allocator);
|
||||
const str = RocStr.init("12345678912345678910", 20);
|
||||
const suffix = RocStr.init("345678912345678910", 18);
|
||||
defer str.deinit();
|
||||
defer suffix.deinit();
|
||||
|
||||
expect(endsWith(str, suffix));
|
||||
}
|
||||
|
||||
test "endsWith: hello world ends with world" {
|
||||
const str = RocStr.init(testing.allocator, "hello world", 11);
|
||||
const suffix = RocStr.init(testing.allocator, "world", 5);
|
||||
defer str.deinit(testing.allocator);
|
||||
defer suffix.deinit(testing.allocator);
|
||||
const str = RocStr.init("hello world", 11);
|
||||
const suffix = RocStr.init("world", 5);
|
||||
defer str.deinit();
|
||||
defer suffix.deinit();
|
||||
|
||||
expect(endsWith(str, suffix));
|
||||
}
|
||||
|
||||
// Str.concat
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strConcatC(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr {
|
||||
return @call(.{ .modifier = always_inline }, strConcat, .{ std.heap.c_allocator, result_in_place, arg1, arg2 });
|
||||
return @call(.{ .modifier = always_inline }, strConcat, .{ result_in_place, arg1, arg2 });
|
||||
}
|
||||
|
||||
fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
|
||||
fn strConcat(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
|
||||
if (arg1.isEmpty()) {
|
||||
// the second argument is borrowed, so we must increment its refcount before returning
|
||||
return RocStr.clone(allocator, result_in_place, arg2);
|
||||
return RocStr.clone(result_in_place, arg2);
|
||||
} else if (arg2.isEmpty()) {
|
||||
// the first argument is owned, so we can return it without cloning
|
||||
return RocStr.clone(allocator, result_in_place, arg1);
|
||||
return RocStr.clone(result_in_place, arg1);
|
||||
} else {
|
||||
const combined_length = arg1.len() + arg2.len();
|
||||
|
||||
const alignment = 1;
|
||||
const element_width = 1;
|
||||
|
||||
if (!arg1.isSmallStr() and arg1.isUnique()) {
|
||||
if (arg1.str_bytes) |source_ptr| {
|
||||
const new_source = utils.unsafeReallocate(
|
||||
source_ptr,
|
||||
allocator,
|
||||
alignment,
|
||||
RocStr.alignment,
|
||||
arg1.len(),
|
||||
combined_length,
|
||||
element_width,
|
||||
|
@ -954,16 +942,13 @@ fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2
|
|||
}
|
||||
}
|
||||
|
||||
var result = arg1.reallocateFresh(
|
||||
allocator,
|
||||
combined_length,
|
||||
);
|
||||
var result = arg1.reallocateFresh(combined_length);
|
||||
var result_ptr = result.asU8ptr();
|
||||
|
||||
arg1.memcpy(result_ptr);
|
||||
arg2.memcpy(result_ptr + arg1.len());
|
||||
|
||||
arg1.deinit(allocator);
|
||||
arg1.deinit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -973,27 +958,27 @@ test "RocStr.concat: small concat small" {
|
|||
const str1_len = 3;
|
||||
var str1: [str1_len]u8 = "foo".*;
|
||||
const str1_ptr: [*]u8 = &str1;
|
||||
var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
|
||||
var roc_str1 = RocStr.init(str1_ptr, str1_len);
|
||||
|
||||
const str2_len = 3;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
const str2_ptr: [*]u8 = &str2;
|
||||
var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
|
||||
var roc_str2 = RocStr.init(str2_ptr, str2_len);
|
||||
|
||||
const str3_len = 6;
|
||||
var str3: [str3_len]u8 = "fooabc".*;
|
||||
const str3_ptr: [*]u8 = &str3;
|
||||
var roc_str3 = RocStr.init(testing.allocator, str3_ptr, str3_len);
|
||||
var roc_str3 = RocStr.init(str3_ptr, str3_len);
|
||||
|
||||
defer {
|
||||
roc_str1.deinit(testing.allocator);
|
||||
roc_str2.deinit(testing.allocator);
|
||||
roc_str3.deinit(testing.allocator);
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
roc_str3.deinit();
|
||||
}
|
||||
|
||||
const result = strConcat(testing.allocator, InPlace.Clone, roc_str1, roc_str2);
|
||||
const result = strConcat(InPlace.Clone, roc_str1, roc_str2);
|
||||
|
||||
defer result.deinit(testing.allocator);
|
||||
defer result.deinit();
|
||||
|
||||
expect(roc_str3.eq(result));
|
||||
}
|
||||
|
@ -1004,12 +989,11 @@ pub const RocListStr = extern struct {
|
|||
};
|
||||
|
||||
// Str.joinWith
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strJoinWithC(list: RocListStr, separator: RocStr) callconv(.C) RocStr {
|
||||
return @call(.{ .modifier = always_inline }, strJoinWith, .{ std.heap.c_allocator, list, separator });
|
||||
return @call(.{ .modifier = always_inline }, strJoinWith, .{ list, separator });
|
||||
}
|
||||
|
||||
fn strJoinWith(allocator: *Allocator, list: RocListStr, separator: RocStr) RocStr {
|
||||
fn strJoinWith(list: RocListStr, separator: RocStr) RocStr {
|
||||
const len = list.list_length;
|
||||
|
||||
if (len == 0) {
|
||||
|
@ -1027,7 +1011,7 @@ fn strJoinWith(allocator: *Allocator, list: RocListStr, separator: RocStr) RocSt
|
|||
// include size of the separator
|
||||
total_size += separator.len() * (len - 1);
|
||||
|
||||
var result = RocStr.allocate(allocator, InPlace.Clone, total_size);
|
||||
var result = RocStr.allocate(InPlace.Clone, total_size);
|
||||
var result_ptr = result.asU8ptr();
|
||||
|
||||
var offset: usize = 0;
|
||||
|
@ -1050,45 +1034,45 @@ test "RocStr.joinWith: result is big" {
|
|||
const sep_len = 2;
|
||||
var sep: [sep_len]u8 = ", ".*;
|
||||
const sep_ptr: [*]u8 = &sep;
|
||||
var roc_sep = RocStr.init(testing.allocator, sep_ptr, sep_len);
|
||||
var roc_sep = RocStr.init(sep_ptr, sep_len);
|
||||
|
||||
const elem_len = 13;
|
||||
var elem: [elem_len]u8 = "foobarbazspam".*;
|
||||
const elem_ptr: [*]u8 = &elem;
|
||||
var roc_elem = RocStr.init(testing.allocator, elem_ptr, elem_len);
|
||||
var roc_elem = RocStr.init(elem_ptr, elem_len);
|
||||
|
||||
const result_len = 43;
|
||||
var xresult: [result_len]u8 = "foobarbazspam, foobarbazspam, foobarbazspam".*;
|
||||
const result_ptr: [*]u8 = &xresult;
|
||||
var roc_result = RocStr.init(testing.allocator, result_ptr, result_len);
|
||||
var roc_result = RocStr.init(result_ptr, result_len);
|
||||
|
||||
var elements: [3]RocStr = .{ roc_elem, roc_elem, roc_elem };
|
||||
const list = RocListStr{ .list_length = 3, .list_elements = @ptrCast([*]RocStr, &elements) };
|
||||
|
||||
defer {
|
||||
roc_sep.deinit(testing.allocator);
|
||||
roc_elem.deinit(testing.allocator);
|
||||
roc_result.deinit(testing.allocator);
|
||||
roc_sep.deinit();
|
||||
roc_elem.deinit();
|
||||
roc_result.deinit();
|
||||
}
|
||||
|
||||
const result = strJoinWith(testing.allocator, list, roc_sep);
|
||||
const result = strJoinWith(list, roc_sep);
|
||||
|
||||
defer result.deinit(testing.allocator);
|
||||
defer result.deinit();
|
||||
|
||||
expect(roc_result.eq(result));
|
||||
}
|
||||
|
||||
// Str.toBytes
|
||||
pub fn strToBytesC(arg: RocStr) callconv(.C) RocList {
|
||||
return @call(.{ .modifier = always_inline }, strToBytes, .{ std.heap.c_allocator, arg });
|
||||
return @call(.{ .modifier = always_inline }, strToBytes, .{ arg });
|
||||
}
|
||||
|
||||
fn strToBytes(allocator: *Allocator, arg: RocStr) RocList {
|
||||
fn strToBytes(arg: RocStr) RocList {
|
||||
if (arg.isEmpty()) {
|
||||
return RocList.empty();
|
||||
} else if (arg.isSmallStr()) {
|
||||
const length = arg.len();
|
||||
const ptr = utils.allocateWithRefcount(allocator, @alignOf(usize), length);
|
||||
const ptr = utils.allocateWithRefcount(RocStr.alignment, length);
|
||||
|
||||
@memcpy(ptr, arg.asU8ptr(), length);
|
||||
|
||||
|
@ -1106,25 +1090,25 @@ const FromUtf8Result = extern struct {
|
|||
};
|
||||
|
||||
pub fn fromUtf8C(arg: RocList, output: *FromUtf8Result) callconv(.C) void {
|
||||
output.* = @call(.{ .modifier = always_inline }, fromUtf8, .{ std.heap.c_allocator, arg });
|
||||
output.* = @call(.{ .modifier = always_inline }, fromUtf8, .{ arg });
|
||||
}
|
||||
|
||||
fn fromUtf8(allocator: *Allocator, arg: RocList) FromUtf8Result {
|
||||
fn fromUtf8(arg: RocList) FromUtf8Result {
|
||||
const bytes = @ptrCast([*]const u8, arg.bytes)[0..arg.length];
|
||||
|
||||
if (unicode.utf8ValidateSlice(bytes)) {
|
||||
// the output will be correct. Now we need to take ownership of the input
|
||||
if (arg.len() <= SMALL_STR_MAX_LENGTH) {
|
||||
// turn the bytes into a small string
|
||||
const string = RocStr.init(allocator, @ptrCast([*]u8, arg.bytes), arg.len());
|
||||
const string = RocStr.init(@ptrCast([*]u8, arg.bytes), arg.len());
|
||||
|
||||
// then decrement the input list
|
||||
const data_bytes = arg.len();
|
||||
utils.decref(allocator, @alignOf(usize), arg.bytes, data_bytes);
|
||||
utils.decref(RocStr.alignment, arg.bytes, data_bytes);
|
||||
|
||||
return FromUtf8Result{ .is_ok = true, .string = string, .byte_index = 0, .problem_code = Utf8ByteProblem.InvalidStartByte };
|
||||
} else {
|
||||
const byte_list = arg.makeUnique(allocator, @alignOf(usize), @sizeOf(u8));
|
||||
const byte_list = arg.makeUnique(RocStr.alignment, @sizeOf(u8));
|
||||
|
||||
const string = RocStr{ .str_bytes = byte_list.bytes, .str_len = byte_list.length };
|
||||
|
||||
|
@ -1135,7 +1119,7 @@ fn fromUtf8(allocator: *Allocator, arg: RocList) FromUtf8Result {
|
|||
|
||||
// consume the input list
|
||||
const data_bytes = arg.len();
|
||||
utils.decref(allocator, @alignOf(usize), arg.bytes, data_bytes);
|
||||
utils.decref(RocStr.alignment, arg.bytes, data_bytes);
|
||||
|
||||
return FromUtf8Result{ .is_ok = false, .string = RocStr.empty(), .byte_index = temp.index, .problem_code = temp.problem };
|
||||
}
|
||||
|
@ -1202,11 +1186,11 @@ pub const Utf8ByteProblem = packed enum(u8) {
|
|||
};
|
||||
|
||||
fn validateUtf8Bytes(bytes: [*]u8, length: usize) FromUtf8Result {
|
||||
return fromUtf8(std.testing.allocator, RocList{ .bytes = bytes, .length = length });
|
||||
return fromUtf8(RocList{ .bytes = bytes, .length = length });
|
||||
}
|
||||
|
||||
fn validateUtf8BytesX(str: RocList) FromUtf8Result {
|
||||
return fromUtf8(std.testing.allocator, str);
|
||||
return fromUtf8(str);
|
||||
}
|
||||
|
||||
fn expectOk(result: FromUtf8Result) void {
|
||||
|
@ -1214,7 +1198,7 @@ fn expectOk(result: FromUtf8Result) void {
|
|||
}
|
||||
|
||||
fn sliceHelp(bytes: [*]const u8, length: usize) RocList {
|
||||
var list = RocList.allocate(testing.allocator, @alignOf(usize), length, @sizeOf(u8));
|
||||
var list = RocList.allocate(RocStr.alignment, length, @sizeOf(u8));
|
||||
@memcpy(list.bytes orelse unreachable, bytes, length);
|
||||
list.length = length;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue