mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
add baseline of seamless slices
This commit is contained in:
parent
e32c5f6514
commit
4ced1bcfdd
4 changed files with 72 additions and 42 deletions
|
@ -18,18 +18,29 @@ const HasTagId = fn (u16, ?[*]u8) callconv(.C) extern struct { matched: bool, da
|
||||||
pub const RocList = extern struct {
|
pub const RocList = extern struct {
|
||||||
bytes: ?[*]u8,
|
bytes: ?[*]u8,
|
||||||
length: usize,
|
length: usize,
|
||||||
capacity: usize,
|
capacityOrRefPtr: usize,
|
||||||
|
|
||||||
pub inline fn len(self: RocList) usize {
|
pub inline fn len(self: RocList) usize {
|
||||||
return self.length;
|
return self.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getCapacity(self: RocList) usize {
|
||||||
|
if (!self.isSeamlessSlice()) {
|
||||||
|
return self.capacityOrRefPtr;
|
||||||
|
}
|
||||||
|
return self.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isSeamlessSlice(self: RocList) bool {
|
||||||
|
return @bitCast(isize, self.capacityOrRefPtr) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn isEmpty(self: RocList) bool {
|
pub fn isEmpty(self: RocList) bool {
|
||||||
return self.len() == 0;
|
return self.len() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn empty() RocList {
|
pub fn empty() RocList {
|
||||||
return RocList{ .bytes = null, .length = 0, .capacity = 0 };
|
return RocList{ .bytes = null, .length = 0, .capacityOrRefPtr = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eql(self: RocList, other: RocList) bool {
|
pub fn eql(self: RocList, other: RocList) bool {
|
||||||
|
@ -76,7 +87,12 @@ pub const RocList = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decref(self: RocList, alignment: u32) void {
|
pub fn decref(self: RocList, alignment: u32) void {
|
||||||
utils.decref(self.bytes, self.capacity(), alignment);
|
if (self.isSeamlessSlice()) {
|
||||||
|
const ref_ptr = @intToPtr([*]isize, self.capacityOrRefPtr << 1);
|
||||||
|
utils.decref_ptr_to_refcount(ref_ptr, alignment);
|
||||||
|
} else {
|
||||||
|
utils.decref(self.bytes, self.getCapacity(), alignment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elements(self: RocList, comptime T: type) ?[*]T {
|
pub fn elements(self: RocList, comptime T: type) ?[*]T {
|
||||||
|
@ -88,7 +104,7 @@ pub const RocList = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refcountMachine(self: RocList) usize {
|
fn refcountMachine(self: RocList) usize {
|
||||||
if (self.capacity == 0) {
|
if (self.getCapacity() == 0) {
|
||||||
// the zero-capacity is Clone, copying it will not leak memory
|
// the zero-capacity is Clone, copying it will not leak memory
|
||||||
return utils.REFCOUNT_ONE;
|
return utils.REFCOUNT_ONE;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +163,7 @@ pub const RocList = extern struct {
|
||||||
return RocList{
|
return RocList{
|
||||||
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
|
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
|
||||||
.length = length,
|
.length = length,
|
||||||
.capacity = capacity,
|
.capacityOrRefPtr = capacity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,13 +174,14 @@ pub const RocList = extern struct {
|
||||||
element_width: usize,
|
element_width: usize,
|
||||||
) RocList {
|
) RocList {
|
||||||
if (self.bytes) |source_ptr| {
|
if (self.bytes) |source_ptr| {
|
||||||
if (self.isUnique()) {
|
if (self.isUnique() and !self.isSeamlessSlice()) {
|
||||||
if (self.capacity >= new_length) {
|
const capacity = self.getCapacity();
|
||||||
return RocList{ .bytes = self.bytes, .length = new_length, .capacity = self.capacity };
|
if (capacity >= new_length) {
|
||||||
|
return RocList{ .bytes = self.bytes, .length = new_length, .capacityOrRefPtr = capacity };
|
||||||
} else {
|
} else {
|
||||||
const new_capacity = utils.calculateCapacity(self.capacity, new_length, element_width);
|
const new_capacity = utils.calculateCapacity(capacity, new_length, element_width);
|
||||||
const new_source = utils.unsafeReallocate(source_ptr, alignment, self.capacity, new_capacity, element_width);
|
const new_source = utils.unsafeReallocate(source_ptr, alignment, capacity, new_capacity, element_width);
|
||||||
return RocList{ .bytes = new_source, .length = new_length, .capacity = new_capacity };
|
return RocList{ .bytes = new_source, .length = new_length, .capacityOrRefPtr = new_capacity };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.reallocateFresh(alignment, new_length, element_width);
|
return self.reallocateFresh(alignment, new_length, element_width);
|
||||||
|
@ -427,7 +444,7 @@ pub fn listReserve(
|
||||||
update_mode: UpdateMode,
|
update_mode: UpdateMode,
|
||||||
) callconv(.C) RocList {
|
) callconv(.C) RocList {
|
||||||
const old_length = list.len();
|
const old_length = list.len();
|
||||||
if ((update_mode == .InPlace or list.isUnique()) and list.capacity >= list.len() + spare) {
|
if ((update_mode == .InPlace or list.isUnique()) and list.getCapacity() >= list.len() + spare) {
|
||||||
return list;
|
return list;
|
||||||
} else {
|
} else {
|
||||||
var output = list.reallocate(alignment, old_length + spare, element_width);
|
var output = list.reallocate(alignment, old_length + spare, element_width);
|
||||||
|
@ -461,10 +478,12 @@ fn listAppend(list: RocList, alignment: u32, element: Opaque, element_width: usi
|
||||||
|
|
||||||
pub fn listPrepend(list: RocList, alignment: u32, element: Opaque, element_width: usize) callconv(.C) RocList {
|
pub fn listPrepend(list: RocList, alignment: u32, element: Opaque, element_width: usize) callconv(.C) RocList {
|
||||||
const old_length = list.len();
|
const old_length = list.len();
|
||||||
var output = list.reallocate(alignment, old_length + 1, element_width);
|
// TODO: properly wire in update mode.
|
||||||
|
var with_capacity = listReserve(list, alignment, 1, element_width, .Immutable);
|
||||||
|
with_capacity.length += 1;
|
||||||
|
|
||||||
// can't use one memcpy here because source and target overlap
|
// can't use one memcpy here because source and target overlap
|
||||||
if (output.bytes) |target| {
|
if (with_capacity.bytes) |target| {
|
||||||
var i: usize = old_length;
|
var i: usize = old_length;
|
||||||
|
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
|
@ -480,7 +499,7 @@ pub fn listPrepend(list: RocList, alignment: u32, element: Opaque, element_width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return with_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listSwap(
|
pub fn listSwap(
|
||||||
|
@ -519,6 +538,11 @@ pub fn listSublist(
|
||||||
len: usize,
|
len: usize,
|
||||||
dec: Dec,
|
dec: Dec,
|
||||||
) callconv(.C) RocList {
|
) callconv(.C) RocList {
|
||||||
|
// Alignment is no longer used.
|
||||||
|
// This will never free the original list.
|
||||||
|
// It will either return a seamless slice or an empty list with capacity.
|
||||||
|
_ = alignment;
|
||||||
|
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
if (len == 0 or start >= size) {
|
if (len == 0 or start >= size) {
|
||||||
if (list.isUnique()) {
|
if (list.isUnique()) {
|
||||||
|
@ -556,26 +580,28 @@ pub fn listSublist(
|
||||||
dec(element);
|
dec(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.isUnique()) {
|
if (list.isUnique() and start == 0) {
|
||||||
var output = list;
|
var output = list;
|
||||||
output.length = keep_len;
|
output.length = keep_len;
|
||||||
if (start == 0) {
|
|
||||||
return output;
|
|
||||||
} else {
|
|
||||||
// We want memmove due to aliasing. Zig does not expose it directly.
|
|
||||||
// Instead use copy which can write to aliases as long as the dest is before the source.
|
|
||||||
mem.copy(u8, source_ptr[0 .. keep_len * element_width], source_ptr[start * element_width .. (start + keep_len) * element_width]);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const output = RocList.allocate(alignment, keep_len, element_width);
|
|
||||||
const target_ptr = output.bytes orelse unreachable;
|
|
||||||
|
|
||||||
@memcpy(target_ptr, source_ptr + start * element_width, keep_len * element_width);
|
|
||||||
|
|
||||||
list.decref(alignment);
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
} else {
|
||||||
|
if (list.isSeamlessSlice()) {
|
||||||
|
return RocList{
|
||||||
|
.bytes = source_ptr + start * element_width,
|
||||||
|
.length = keep_len,
|
||||||
|
.capacityOrRefPtr = list.capacityOrRefPtr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ref_ptr = @ptrCast([*]isize, @alignCast(@alignOf(isize), source_ptr)) - 1;
|
||||||
|
// This should be a usize with only the highest bit set.
|
||||||
|
const seamless_slice_bit =
|
||||||
|
@bitCast(usize, @as(isize, std.math.minInt(isize)));
|
||||||
|
return RocList{
|
||||||
|
.bytes = source_ptr + start * element_width,
|
||||||
|
.length = keep_len,
|
||||||
|
.capacityOrRefPtr = (@ptrToInt(ref_ptr) >> 1) | seamless_slice_bit,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +772,7 @@ fn swapElements(source_ptr: [*]u8, element_width: usize, index_1: usize, index_2
|
||||||
pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_width: usize) callconv(.C) RocList {
|
pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_width: usize) callconv(.C) RocList {
|
||||||
// NOTE we always use list_a! because it is owned, we must consume it, and it may have unused capacity
|
// NOTE we always use list_a! because it is owned, we must consume it, and it may have unused capacity
|
||||||
if (list_b.isEmpty()) {
|
if (list_b.isEmpty()) {
|
||||||
if (list_a.capacity == 0) {
|
if (list_a.getCapacity() == 0) {
|
||||||
return list_b;
|
return list_b;
|
||||||
} else {
|
} else {
|
||||||
// we must consume this list. Even though it has no elements, it could still have capacity
|
// we must consume this list. Even though it has no elements, it could still have capacity
|
||||||
|
|
|
@ -1665,7 +1665,7 @@ test "RocStr.concat: small concat small" {
|
||||||
pub const RocListStr = extern struct {
|
pub const RocListStr = extern struct {
|
||||||
list_elements: ?[*]RocStr,
|
list_elements: ?[*]RocStr,
|
||||||
list_length: usize,
|
list_length: usize,
|
||||||
list_capacity: usize,
|
list_capacityOrRefPtr: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Str.joinWith
|
// Str.joinWith
|
||||||
|
@ -1673,7 +1673,7 @@ pub fn strJoinWithC(list: RocList, separator: RocStr) callconv(.C) RocStr {
|
||||||
const roc_list_str = RocListStr{
|
const roc_list_str = RocListStr{
|
||||||
.list_elements = @ptrCast(?[*]RocStr, @alignCast(@alignOf(usize), list.bytes)),
|
.list_elements = @ptrCast(?[*]RocStr, @alignCast(@alignOf(usize), list.bytes)),
|
||||||
.list_length = list.length,
|
.list_length = list.length,
|
||||||
.list_capacity = list.capacity,
|
.list_capacityOrRefPtr = list.capacityOrRefPtr,
|
||||||
};
|
};
|
||||||
|
|
||||||
return @call(.{ .modifier = always_inline }, strJoinWith, .{ roc_list_str, separator });
|
return @call(.{ .modifier = always_inline }, strJoinWith, .{ roc_list_str, separator });
|
||||||
|
@ -1735,7 +1735,7 @@ test "RocStr.joinWith: result is big" {
|
||||||
var elements: [3]RocStr = .{ roc_elem, roc_elem, roc_elem };
|
var elements: [3]RocStr = .{ roc_elem, roc_elem, roc_elem };
|
||||||
const list = RocListStr{
|
const list = RocListStr{
|
||||||
.list_length = 3,
|
.list_length = 3,
|
||||||
.list_capacity = 3,
|
.list_capacityOrRefPtr = 3,
|
||||||
.list_elements = @ptrCast([*]RocStr, &elements),
|
.list_elements = @ptrCast([*]RocStr, &elements),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1766,9 +1766,9 @@ inline fn strToBytes(arg: RocStr) RocList {
|
||||||
|
|
||||||
@memcpy(ptr, arg.asU8ptr(), length);
|
@memcpy(ptr, arg.asU8ptr(), length);
|
||||||
|
|
||||||
return RocList{ .length = length, .bytes = ptr, .capacity = length };
|
return RocList{ .length = length, .bytes = ptr, .capacityOrRefPtr = length };
|
||||||
} else {
|
} else {
|
||||||
return RocList{ .length = length, .bytes = arg.str_bytes, .capacity = arg.str_capacity };
|
return RocList{ .length = length, .bytes = arg.str_bytes, .capacityOrRefPtr = arg.str_capacity };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1809,10 +1809,11 @@ inline fn fromUtf8(arg: RocList, update_mode: UpdateMode) FromUtf8Result {
|
||||||
} else {
|
} else {
|
||||||
const byte_list = arg.makeUniqueExtra(RocStr.alignment, @sizeOf(u8), update_mode);
|
const byte_list = arg.makeUniqueExtra(RocStr.alignment, @sizeOf(u8), update_mode);
|
||||||
|
|
||||||
|
// TODO: hangle seamless slice conversion
|
||||||
const string = RocStr{
|
const string = RocStr{
|
||||||
.str_bytes = byte_list.bytes,
|
.str_bytes = byte_list.bytes,
|
||||||
.str_len = byte_list.length,
|
.str_len = byte_list.length,
|
||||||
.str_capacity = byte_list.capacity,
|
.str_capacity = byte_list.capacityOrRefPtr,
|
||||||
};
|
};
|
||||||
|
|
||||||
return FromUtf8Result{
|
return FromUtf8Result{
|
||||||
|
@ -1856,10 +1857,11 @@ pub fn fromUtf8Range(arg: RocList, start: usize, count: usize, update_mode: Upda
|
||||||
if (count == arg.len() and count > SMALL_STR_MAX_LENGTH) {
|
if (count == arg.len() and count > SMALL_STR_MAX_LENGTH) {
|
||||||
const byte_list = arg.makeUniqueExtra(RocStr.alignment, @sizeOf(u8), update_mode);
|
const byte_list = arg.makeUniqueExtra(RocStr.alignment, @sizeOf(u8), update_mode);
|
||||||
|
|
||||||
|
// TODO: hangle seamless slice conversion
|
||||||
const string = RocStr{
|
const string = RocStr{
|
||||||
.str_bytes = byte_list.bytes,
|
.str_bytes = byte_list.bytes,
|
||||||
.str_len = byte_list.length,
|
.str_len = byte_list.length,
|
||||||
.str_capacity = byte_list.capacity,
|
.str_capacity = byte_list.capacityOrRefPtr,
|
||||||
};
|
};
|
||||||
|
|
||||||
return FromUtf8Result{
|
return FromUtf8Result{
|
||||||
|
@ -1958,7 +1960,7 @@ pub const Utf8ByteProblem = enum(u8) {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn validateUtf8Bytes(bytes: [*]u8, length: usize) FromUtf8Result {
|
fn validateUtf8Bytes(bytes: [*]u8, length: usize) FromUtf8Result {
|
||||||
return fromUtf8(RocList{ .bytes = bytes, .length = length, .capacity = length }, .Immutable);
|
return fromUtf8(RocList{ .bytes = bytes, .length = length, .capacityOrRefPtr = length }, .Immutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validateUtf8BytesX(str: RocList) FromUtf8Result {
|
fn validateUtf8BytesX(str: RocList) FromUtf8Result {
|
||||||
|
|
|
@ -204,7 +204,7 @@ pub fn decref(
|
||||||
decref_ptr_to_refcount(isizes - 1, alignment);
|
decref_ptr_to_refcount(isizes - 1, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn decref_ptr_to_refcount(
|
pub inline fn decref_ptr_to_refcount(
|
||||||
refcount_ptr: [*]isize,
|
refcount_ptr: [*]isize,
|
||||||
alignment: u32,
|
alignment: u32,
|
||||||
) void {
|
) void {
|
||||||
|
|
|
@ -398,6 +398,7 @@ pub(crate) fn list_capacity<'ctx>(
|
||||||
builder: &Builder<'ctx>,
|
builder: &Builder<'ctx>,
|
||||||
wrapper_struct: StructValue<'ctx>,
|
wrapper_struct: StructValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
|
// TODO: This won't work on seemless slices. Needs to return the len if the capacity is negative.
|
||||||
builder
|
builder
|
||||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_CAPACITY, "list_capacity")
|
.build_extract_value(wrapper_struct, Builtin::WRAPPER_CAPACITY, "list_capacity")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -801,6 +802,7 @@ pub(crate) fn decref<'a, 'ctx, 'env>(
|
||||||
wrapper_struct: StructValue<'ctx>,
|
wrapper_struct: StructValue<'ctx>,
|
||||||
alignment: u32,
|
alignment: u32,
|
||||||
) {
|
) {
|
||||||
|
// TODO: This definitely needs to handle seamless slices
|
||||||
let (_, pointer) = load_list(
|
let (_, pointer) = load_list(
|
||||||
env.builder,
|
env.builder,
|
||||||
wrapper_struct,
|
wrapper_struct,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue