Merge pull request #4237 from snprajwal/str-withcapacity

builtin(str): implement Str.withCapacity
This commit is contained in:
Ayaz 2022-10-08 14:31:30 -05:00 committed by GitHub
commit 9d05bcd9d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 59 additions and 2 deletions

View file

@ -144,6 +144,7 @@ comptime {
exportStrFn(str.strTrimLeft, "trim_left"); exportStrFn(str.strTrimLeft, "trim_left");
exportStrFn(str.strTrimRight, "trim_right"); exportStrFn(str.strTrimRight, "trim_right");
exportStrFn(str.strCloneTo, "clone_to"); exportStrFn(str.strCloneTo, "clone_to");
exportStrFn(str.withCapacity, "with_capacity");
inline for (INTEGERS) |T| { inline for (INTEGERS) |T| {
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int."); str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");

View file

@ -2596,6 +2596,10 @@ pub fn reserve(string: RocStr, capacity: usize) callconv(.C) RocStr {
} }
} }
pub fn withCapacity(capacity: usize) callconv(.C) RocStr {
return RocStr.allocate(0, capacity);
}
pub fn getScalarUnsafe(string: RocStr, index: usize) callconv(.C) extern struct { bytesParsed: usize, scalar: u32 } { pub fn getScalarUnsafe(string: RocStr, index: usize) callconv(.C) extern struct { bytesParsed: usize, scalar: u32 } {
const slice = string.asSlice(); const slice = string.asSlice();
const bytesParsed = @intCast(usize, std.unicode.utf8ByteSequenceLength(slice[index]) catch unreachable); const bytesParsed = @intCast(usize, std.unicode.utf8ByteSequenceLength(slice[index]) catch unreachable);

View file

@ -43,6 +43,7 @@ interface Str
appendScalar, appendScalar,
walkScalars, walkScalars,
walkScalarsUntil, walkScalarsUntil,
withCapacity,
] ]
imports [ imports [
Bool.{ Bool }, Bool.{ Bool },
@ -144,6 +145,9 @@ Utf8Problem : { byteIndex : Nat, problem : Utf8ByteProblem }
isEmpty : Str -> Bool isEmpty : Str -> Bool
concat : Str, Str -> Str concat : Str, Str -> Str
## Returns a string of the specified capacity without any content
withCapacity : Nat -> Str
## Combine a list of strings into a single string, with a separator ## Combine a list of strings into a single string, with a separator
## string in between each. ## string in between each.
## ##

View file

@ -361,6 +361,7 @@ pub const STR_RESERVE: &str = "roc_builtins.str.reserve";
pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar"; pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar";
pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe"; pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe";
pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to"; pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to";
pub const STR_WITH_CAPACITY: &str = "roc_builtins.str.with_capacity";
pub const LIST_MAP: &str = "roc_builtins.list.map"; pub const LIST_MAP: &str = "roc_builtins.list.map";
pub const LIST_MAP2: &str = "roc_builtins.list.map2"; pub const LIST_MAP2: &str = "roc_builtins.list.map2";

View file

@ -123,6 +123,7 @@ map_symbol_to_lowlevel_and_arity! {
StrGetScalarUnsafe; STR_GET_SCALAR_UNSAFE; 2, StrGetScalarUnsafe; STR_GET_SCALAR_UNSAFE; 2,
StrToNum; STR_TO_NUM; 1, StrToNum; STR_TO_NUM; 1,
StrGetCapacity; STR_CAPACITY; 1, StrGetCapacity; STR_CAPACITY; 1,
StrWithCapacity; STR_WITH_CAPACITY; 1,
ListLen; LIST_LEN; 1, ListLen; LIST_LEN; 1,
ListWithCapacity; LIST_WITH_CAPACITY; 1, ListWithCapacity; LIST_WITH_CAPACITY; 1,

View file

@ -6035,6 +6035,20 @@ fn run_low_level<'a, 'ctx, 'env>(
bitcode::STR_TRIM_RIGHT, bitcode::STR_TRIM_RIGHT,
) )
} }
StrWithCapacity => {
// Str.withCapacity : Nat -> Str
debug_assert_eq!(args.len(), 1);
let str_len = load_symbol(scope, &args[0]);
call_str_bitcode_fn(
env,
&[],
&[str_len],
BitcodeReturns::Str,
bitcode::STR_WITH_CAPACITY,
)
}
ListLen => { ListLen => {
// List.len : List * -> Nat // List.len : List * -> Nat
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
@ -6157,7 +6171,7 @@ fn run_low_level<'a, 'ctx, 'env>(
list_prepend(env, original_wrapper, elem, elem_layout) list_prepend(env, original_wrapper, elem, elem_layout)
} }
StrGetUnsafe => { StrGetUnsafe => {
// List.getUnsafe : Str, Nat -> u8 // Str.getUnsafe : Str, Nat -> u8
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
let wrapper_struct = load_symbol(scope, &args[0]); let wrapper_struct = load_symbol(scope, &args[0]);

View file

@ -302,6 +302,7 @@ impl<'a> LowLevelCall<'a> {
StrSubstringUnsafe => { StrSubstringUnsafe => {
self.load_args_and_call_zig(backend, bitcode::STR_SUBSTRING_UNSAFE) self.load_args_and_call_zig(backend, bitcode::STR_SUBSTRING_UNSAFE)
} }
StrWithCapacity => self.load_args_and_call_zig(backend, bitcode::STR_WITH_CAPACITY),
// List // List
ListLen => match backend.storage.get(&self.arguments[0]) { ListLen => match backend.storage.get(&self.arguments[0]) {

View file

@ -30,6 +30,7 @@ pub enum LowLevel {
StrAppendScalar, StrAppendScalar,
StrGetScalarUnsafe, StrGetScalarUnsafe,
StrGetCapacity, StrGetCapacity,
StrWithCapacity,
ListLen, ListLen,
ListWithCapacity, ListWithCapacity,
ListReserve, ListReserve,
@ -249,6 +250,7 @@ map_symbol_to_lowlevel! {
StrGetScalarUnsafe <= STR_GET_SCALAR_UNSAFE, StrGetScalarUnsafe <= STR_GET_SCALAR_UNSAFE,
StrToNum <= STR_TO_NUM, StrToNum <= STR_TO_NUM,
StrGetCapacity <= STR_CAPACITY, StrGetCapacity <= STR_CAPACITY,
StrWithCapacity <= STR_WITH_CAPACITY,
ListLen <= LIST_LEN, ListLen <= LIST_LEN,
ListGetCapacity <= LIST_CAPACITY, ListGetCapacity <= LIST_CAPACITY,
ListWithCapacity <= LIST_WITH_CAPACITY, ListWithCapacity <= LIST_WITH_CAPACITY,

View file

@ -1296,6 +1296,7 @@ define_builtins! {
50 STR_REPLACE_EACH: "replaceEach" 50 STR_REPLACE_EACH: "replaceEach"
51 STR_REPLACE_FIRST: "replaceFirst" 51 STR_REPLACE_FIRST: "replaceFirst"
52 STR_REPLACE_LAST: "replaceLast" 52 STR_REPLACE_LAST: "replaceLast"
53 STR_WITH_CAPACITY: "withCapacity"
} }
6 LIST: "List" => { 6 LIST: "List" => {
0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias 0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias

View file

@ -881,7 +881,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
Unreachable => arena.alloc_slice_copy(&[irrelevant]), Unreachable => arena.alloc_slice_copy(&[irrelevant]),
ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes | StrCountUtf8Bytes ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes | StrCountUtf8Bytes
| StrGetCapacity | ListGetCapacity => arena.alloc_slice_copy(&[borrowed]), | StrGetCapacity | ListGetCapacity => arena.alloc_slice_copy(&[borrowed]),
ListWithCapacity => arena.alloc_slice_copy(&[irrelevant]), ListWithCapacity | StrWithCapacity => arena.alloc_slice_copy(&[irrelevant]),
ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
StrGetUnsafe | ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), StrGetUnsafe | ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListConcat => arena.alloc_slice_copy(&[owned, owned]), ListConcat => arena.alloc_slice_copy(&[owned, owned]),

View file

@ -1930,3 +1930,31 @@ fn when_on_strings() {
i64 i64
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn with_capacity() {
assert_evals_to!(
indoc!(
r#"
Str.withCapacity 10
"#
),
RocStr::from(""),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn with_capacity_concat() {
assert_evals_to!(
indoc!(
r#"
Str.withCapacity 10 |> Str.concat "Forty-two"
"#
),
RocStr::from("Forty-two"),
RocStr
);
}