mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
add Str.releaseExcessCapacity
This commit is contained in:
parent
a80b25d044
commit
48f17a8e2c
11 changed files with 115 additions and 0 deletions
|
@ -156,6 +156,7 @@ comptime {
|
|||
exportStrFn(str.withCapacity, "with_capacity");
|
||||
exportStrFn(str.strGraphemes, "graphemes");
|
||||
exportStrFn(str.strRefcountPtr, "refcount_ptr");
|
||||
exportStrFn(str.strReleaseExcessCapacity, "release_excess_capacity");
|
||||
|
||||
inline for (INTEGERS) |T| {
|
||||
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");
|
||||
|
|
|
@ -121,6 +121,22 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
// allocate space for a (big or small) RocStr, but put nothing in it yet.
|
||||
// Will have the exact same capacity as length if it is not a small string.
|
||||
pub fn allocateExact(length: usize) RocStr {
|
||||
const result_is_big = length >= SMALL_STRING_SIZE;
|
||||
|
||||
if (result_is_big) {
|
||||
return RocStr.allocateBig(length, length);
|
||||
} else {
|
||||
var string = RocStr.empty();
|
||||
|
||||
string.asU8ptrMut()[@sizeOf(RocStr) - 1] = @intCast(u8, length) | 0b1000_0000;
|
||||
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
// This returns all ones if the list is a seamless slice.
|
||||
// Otherwise, it returns all zeros.
|
||||
// This is done without branching for optimization purposes.
|
||||
|
@ -2891,3 +2907,27 @@ pub fn strRefcountPtr(
|
|||
) callconv(.C) ?[*]u8 {
|
||||
return string.getRefcountPtr();
|
||||
}
|
||||
|
||||
pub fn strReleaseExcessCapacity(
|
||||
string: RocStr,
|
||||
) callconv(.C) RocStr {
|
||||
const old_length = string.len();
|
||||
// We use the direct list.capacity_or_ref_ptr to make sure both that there is no extra capacity and that it isn't a seamless slice.
|
||||
if (string.isSmallStr()) {
|
||||
// SmallStr has no excess capacity.
|
||||
return string;
|
||||
} else if (string.isUnique() and !string.isSeamlessSlice() and string.getCapacity() == old_length) {
|
||||
return string;
|
||||
} else if (old_length == 0) {
|
||||
string.decref();
|
||||
return RocStr.empty();
|
||||
} else {
|
||||
var output = RocStr.allocateExact(old_length);
|
||||
const source_ptr = string.asU8ptr();
|
||||
const dest_ptr = output.asU8ptrMut();
|
||||
|
||||
@memcpy(dest_ptr, source_ptr, old_length);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ interface Str
|
|||
splitLast,
|
||||
walkUtf8WithIndex,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
appendScalar,
|
||||
walkScalars,
|
||||
walkScalarsUntil,
|
||||
|
@ -746,6 +747,10 @@ walkUtf8WithIndexHelp = \string, state, step, index, length ->
|
|||
## Enlarge a string for at least the given number additional bytes.
|
||||
reserve : Str, Nat -> Str
|
||||
|
||||
## Shrink the memory footprint of a str such that it's capacity and length are equal.
|
||||
## Note: This will also convert seamless slices to regular lists.
|
||||
releaseExcessCapacity : Str -> Str
|
||||
|
||||
## is UB when the scalar is invalid
|
||||
appendScalarUnsafe : Str, U32 -> Str
|
||||
|
||||
|
|
|
@ -334,6 +334,7 @@ pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to";
|
|||
pub const STR_WITH_CAPACITY: &str = "roc_builtins.str.with_capacity";
|
||||
pub const STR_GRAPHEMES: &str = "roc_builtins.str.graphemes";
|
||||
pub const STR_REFCOUNT_PTR: &str = "roc_builtins.str.refcount_ptr";
|
||||
pub const STR_RELEASE_EXCESS_CAPACITY: &str = "roc_builtins.str.release_excess_capacity";
|
||||
|
||||
pub const LIST_MAP: &str = "roc_builtins.list.map";
|
||||
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
|
||||
|
|
|
@ -126,6 +126,7 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
StrGetCapacity; STR_CAPACITY; 1,
|
||||
StrWithCapacity; STR_WITH_CAPACITY; 1,
|
||||
StrGraphemes; STR_GRAPHEMES; 1,
|
||||
StrReleaseExcessCapacity; STR_RELEASE_EXCESS_CAPACITY; 1,
|
||||
|
||||
ListLen; LIST_LEN; 1,
|
||||
ListWithCapacity; LIST_WITH_CAPACITY; 1,
|
||||
|
|
|
@ -559,6 +559,18 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
|||
bitcode::STR_RESERVE,
|
||||
)
|
||||
}
|
||||
StrReleaseExcessCapacity => {
|
||||
// Str.releaseExcessCapacity: Str -> Str
|
||||
arguments!(string);
|
||||
|
||||
call_str_bitcode_fn(
|
||||
env,
|
||||
&[string],
|
||||
&[],
|
||||
BitcodeReturns::Str,
|
||||
bitcode::STR_RELEASE_EXCESS_CAPACITY,
|
||||
)
|
||||
}
|
||||
StrAppendScalar => {
|
||||
// Str.appendScalar : Str, U32 -> Str
|
||||
arguments!(string, capacity);
|
||||
|
|
|
@ -285,6 +285,9 @@ impl<'a> LowLevelCall<'a> {
|
|||
StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT),
|
||||
StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8),
|
||||
StrReserve => self.load_args_and_call_zig(backend, bitcode::STR_RESERVE),
|
||||
StrReleaseExcessCapacity => {
|
||||
self.load_args_and_call_zig(backend, bitcode::STR_RELEASE_EXCESS_CAPACITY)
|
||||
}
|
||||
StrRepeat => self.load_args_and_call_zig(backend, bitcode::STR_REPEAT),
|
||||
StrAppendScalar => self.load_args_and_call_zig(backend, bitcode::STR_APPEND_SCALAR),
|
||||
StrTrim => self.load_args_and_call_zig(backend, bitcode::STR_TRIM),
|
||||
|
|
|
@ -32,6 +32,7 @@ pub enum LowLevel {
|
|||
StrGetCapacity,
|
||||
StrWithCapacity,
|
||||
StrGraphemes,
|
||||
StrReleaseExcessCapacity,
|
||||
ListLen,
|
||||
ListWithCapacity,
|
||||
ListReserve,
|
||||
|
@ -259,6 +260,7 @@ map_symbol_to_lowlevel! {
|
|||
StrGetCapacity <= STR_CAPACITY,
|
||||
StrWithCapacity <= STR_WITH_CAPACITY,
|
||||
StrGraphemes <= STR_GRAPHEMES,
|
||||
StrReleaseExcessCapacity <= STR_RELEASE_EXCESS_CAPACITY,
|
||||
ListLen <= LIST_LEN,
|
||||
ListGetCapacity <= LIST_CAPACITY,
|
||||
ListWithCapacity <= LIST_WITH_CAPACITY,
|
||||
|
|
|
@ -1327,6 +1327,7 @@ define_builtins! {
|
|||
54 STR_WITH_PREFIX: "withPrefix"
|
||||
55 STR_GRAPHEMES: "graphemes"
|
||||
56 STR_IS_VALID_SCALAR: "isValidScalar"
|
||||
57 STR_RELEASE_EXCESS_CAPACITY: "releaseExcessCapacity"
|
||||
}
|
||||
6 LIST: "List" => {
|
||||
0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias
|
||||
|
|
|
@ -1017,6 +1017,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||
ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||
ListReleaseExcessCapacity => arena.alloc_slice_copy(&[owned]),
|
||||
StrReleaseExcessCapacity => arena.alloc_slice_copy(&[owned]),
|
||||
|
||||
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||
|
||||
|
|
|
@ -2035,3 +2035,51 @@ fn destructure_pattern_assigned_from_thunk_tag() {
|
|||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn release_excess_capacity() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Str.reserve "" 50
|
||||
|> Str.releaseExcessCapacity
|
||||
"#
|
||||
),
|
||||
(RocStr::empty().capacity(), RocStr::empty()),
|
||||
RocStr,
|
||||
|value: RocStr| (value.capacity(), value)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn release_excess_capacity_with_len() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
"123456789012345678901234567890"
|
||||
|> Str.reserve 50
|
||||
|> Str.releaseExcessCapacity
|
||||
"#
|
||||
),
|
||||
(30, "123456789012345678901234567890".into()),
|
||||
RocStr,
|
||||
|value: RocStr| (value.capacity(), value)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn release_excess_capacity_empty() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Str.releaseExcessCapacity ""
|
||||
"#
|
||||
),
|
||||
(RocStr::empty().capacity(), RocStr::empty()),
|
||||
RocStr,
|
||||
|value: RocStr| (value.capacity(), value)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue