Skip to content

Commit 363754c

Browse files
authored
Merge pull request #359 from koto-lang/inline-strings
Inline strings
2 parents 170c094 + 6fb9b5f commit 363754c

File tree

12 files changed

+255
-164
lines changed

12 files changed

+255
-164
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The Koto project adheres to
1515
- Type hints with runtime type checks have been added ([#298](https://github.com/koto-lang/koto/issues/298)).
1616
- Thanks to [@Tarbetu](https://github.com/Tarbetu) for the contributions.
1717
- `export` can be used with multi-assignment expressions.
18-
- e.g. expressions like `export a, b, c = foo()` are now allowed.
18+
- E.g. expressions like `export a, b, c = foo()` are now allowed.
1919
- Maps now support `[]` indexing, returning the Nth entry as a tuple.
2020
- Objects that implement `KotoObject::call` can now be used in operations that
2121
expect functions.
@@ -61,6 +61,7 @@ The Koto project adheres to
6161
`unexpected_args_after_instance`.
6262
- `From` impls for `KNumber` now saturate integer values that are out of the
6363
target type's bounds, instead of wrapping.
64+
- `KString` will now inline short strings to reduce allocations.
6465

6566
### Removed
6667

crates/derive/src/koto_impl.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::PREFIX_FUNCTION;
21
use proc_macro::TokenStream;
32
use quote::{format_ident, quote};
43
use syn::{
@@ -7,6 +6,8 @@ use syn::{
76
Type, TypePath,
87
};
98

9+
const PREFIX_FUNCTION: &str = "__koto_";
10+
1011
struct KotoImplParser {
1112
runtime_path: Path,
1213
}

crates/derive/src/koto_type.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use crate::{attributes::koto_derive_attributes, PREFIX_STATIC};
1+
use crate::attributes::koto_derive_attributes;
22
use proc_macro::TokenStream;
3-
use quote::{format_ident, quote};
3+
use quote::quote;
44
use syn::{parse_macro_input, DeriveInput};
55

66
pub fn derive_koto_type(input: TokenStream) -> TokenStream {
@@ -14,23 +14,37 @@ pub fn derive_koto_type(input: TokenStream) -> TokenStream {
1414
.type_name
1515
.unwrap_or_else(|| quote!(#name).to_string());
1616

17-
let type_string_name = format_ident!("{PREFIX_STATIC}TYPE_STRING_{}", type_name.to_uppercase());
18-
19-
let result = quote! {
20-
#[automatically_derived]
21-
impl #impl_generics KotoType for #name #ty_generics #where_clause {
22-
fn type_static() -> &'static str {
23-
#type_name
24-
}
25-
26-
fn type_string(&self) -> KString {
27-
#type_string_name.with(KString::clone)
17+
// Short type names don't need to be cached, 22 is the `MAX_INLINE_STRING_LEN` constant
18+
let result = if type_name.len() <= 22 {
19+
quote! {
20+
#[automatically_derived]
21+
impl #impl_generics KotoType for #name #ty_generics #where_clause {
22+
fn type_static() -> &'static str {
23+
#type_name
24+
}
25+
26+
fn type_string(&self) -> KString {
27+
#type_name.into()
28+
}
2829
}
2930
}
31+
} else {
32+
quote! {
33+
#[automatically_derived]
34+
impl #impl_generics KotoType for #name #ty_generics #where_clause {
35+
fn type_static() -> &'static str {
36+
#type_name
37+
}
38+
39+
fn type_string(&self) -> KString {
40+
thread_local! {
41+
static TYPE_NAME: KString = #type_name.into();
42+
}
43+
44+
TYPE_NAME.with(KString::clone)
45+
}
46+
}
3047

31-
#[automatically_derived]
32-
thread_local! {
33-
static #type_string_name: KString = #type_name.into();
3448
}
3549
};
3650

crates/derive/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,3 @@ pub fn koto_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
142142
pub fn koto_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
143143
item
144144
}
145-
146-
const PREFIX_STATIC: &str = "__KOTO_";
147-
const PREFIX_FUNCTION: &str = "__koto_";

crates/parser/src/constant_pool.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ enum ConstantEntry {
5252
F64(f64),
5353
// An i64 constant
5454
I64(i64),
55-
// The range in bytes in the ConstantPool's string data for a string constant
55+
// The range in bytes in the `ConstantPool`'s string data for a string constant
5656
Str(Range<usize>),
5757
}
5858

@@ -327,7 +327,7 @@ mod tests {
327327
assert_eq!(ConstantIndex(0), builder.add_string(s1).unwrap());
328328
assert_eq!(ConstantIndex(1), builder.add_string(s2).unwrap());
329329

330-
// don't duplicate string_data
330+
// Don't duplicate string_data
331331
assert_eq!(ConstantIndex(0), builder.add_string(s1).unwrap());
332332
assert_eq!(ConstantIndex(1), builder.add_string(s2).unwrap());
333333

@@ -349,7 +349,7 @@ mod tests {
349349
assert_eq!(ConstantIndex(0), builder.add_i64(n1).unwrap());
350350
assert_eq!(ConstantIndex(1), builder.add_f64(n2).unwrap());
351351

352-
// don't duplicate numbers
352+
// Don't duplicate numbers
353353
assert_eq!(ConstantIndex(0), builder.add_i64(n1).unwrap());
354354
assert_eq!(ConstantIndex(1), builder.add_f64(n2).unwrap());
355355

crates/parser/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ pub enum SyntaxError {
203203
UnterminatedString,
204204
}
205205

206-
/// See [ParserError]
206+
/// See [`ParserError`]
207207
#[derive(Error, Clone, Debug)]
208208
#[allow(missing_docs)]
209209
pub enum ErrorKind {

crates/parser/src/node.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ use crate::{ast::AstIndex, constant_pool::ConstantIndex, StringFormatOptions, St
22
use smallvec::SmallVec;
33
use std::fmt;
44

5-
/// The vec type used in the AST
5+
/// The Vec type used in the AST
66
//
7-
// Q. Why 4 elements in the small vec?
7+
// Q. Why 4 elements in the small Vec?
88
// A. It's the maximum number of elements that can be used in [Node] without increasing its overall
99
// size.
1010
pub type AstVec<T> = SmallVec<[T; 4]>;
1111

12-
/// A convenience macro for initializing [AstVec]s
12+
/// A convenience macro for initializing an [`AstVec`]
1313
pub use smallvec::smallvec as astvec;
1414

1515
/// A parsed node that can be included in the [AST](crate::Ast).
1616
///
17-
/// Nodes refer to each other via [AstIndex]s, see [AstNode](crate::AstNode).
17+
/// Nodes refer to each other via [`AstIndex`], see [`AstNode`](crate::AstNode).
1818
#[derive(Clone, Debug, Default, PartialEq, Eq, derive_name::VariantName)]
1919
pub enum Node {
2020
/// The `null` keyword
@@ -24,14 +24,14 @@ pub enum Node {
2424
/// A single expression wrapped in parentheses
2525
Nested(AstIndex),
2626

27-
/// An identifer, and optionally the type hint node
27+
/// An identifier, and optionally the type hint node
2828
Id(ConstantIndex, Option<AstIndex>),
2929

3030
/// A meta identifier, e.g. `@display` or `@test my_test`
3131
Meta(MetaKeyId, Option<ConstantIndex>),
3232

3333
/// A chained expression, and optionally the node that follows it in the chain
34-
Chain((ChainNode, Option<AstIndex>)), // chain node, next node
34+
Chain((ChainNode, Option<AstIndex>)), // Chain node, next node
3535

3636
/// The `true` keyword
3737
BoolTrue,
@@ -53,12 +53,12 @@ pub enum Node {
5353

5454
/// A list literal
5555
///
56-
/// e.g. `[foo, bar, 42]`
56+
/// E.g. `[foo, bar, 42]`
5757
List(AstVec<AstIndex>),
5858

5959
/// A tuple literal
6060
///
61-
/// e.g. `(foo, bar, 42)`
61+
/// E.g. `(foo, bar, 42)`
6262
///
6363
/// Note that this is also used for implicit tuples, e.g. in `x = 1, 2, 3`
6464
Tuple(AstVec<AstIndex>),
@@ -78,8 +78,8 @@ pub enum Node {
7878
end: AstIndex,
7979
/// Whether or not the end of the range includes the end value itself
8080
///
81-
/// e.g. `1..10` - a range from 1 up to but not including 10
82-
/// e.g. `1..=10` - a range from 1 up to and including 10
81+
/// E.g. `1..10` - a range from 1 up to but not including 10
82+
/// E.g. `1..=10` - a range from 1 up to and including 10
8383
inclusive: bool,
8484
},
8585

@@ -128,15 +128,15 @@ pub enum Node {
128128
/// A block node
129129
///
130130
/// Used for indented blocks that share the context of the frame they're in,
131-
/// e.g. if expressions, arms in match or switch experssions, loop bodies
131+
/// e.g. if expressions, arms in match or switch expressions, loop bodies.
132132
Block(AstVec<AstIndex>),
133133

134134
/// A function node
135135
Function(Function),
136136

137137
/// An import expression
138138
///
139-
/// e.g. `from foo.bar import baz, 'qux'
139+
/// E.g. `from foo.bar import baz, 'qux'`
140140
Import {
141141
/// Where the items should be imported from
142142
///
@@ -163,7 +163,7 @@ pub enum Node {
163163

164164
/// A multiple-assignment expression
165165
///
166-
/// e.g. `x, y = foo()`, or `foo, bar, baz = 1, 2, 3`
166+
/// E.g. `x, y = foo()`, or `foo, bar, baz = 1, 2, 3`
167167
MultiAssign {
168168
/// The targets of the assignment
169169
targets: AstVec<AstIndex>,
@@ -269,7 +269,7 @@ pub enum Node {
269269

270270
/// A type hint
271271
///
272-
/// e.g. `let x: Number = 0`
272+
/// E.g. `let x: Number = 0`
273273
/// ^~~ This is the beginning of the type hint
274274
Type(ConstantIndex),
275275
}

crates/parser/src/string_slice.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use koto_memory::Ptr;
2-
use std::ops::Range;
2+
use std::ops::{Deref, Range};
33

44
/// String data with bounds
55
///
@@ -11,7 +11,7 @@ pub struct StringSlice {
1111
}
1212

1313
impl StringSlice {
14-
/// Initalizes a string slice with the given string data and bounds
14+
/// Initializes a string slice with the given string data and bounds
1515
///
1616
/// If the bounds aren't valid for the given string data then None is returned.
1717
pub fn new(string: Ptr<str>, bounds: Range<usize>) -> Option<Self> {
@@ -25,11 +25,11 @@ impl StringSlice {
2525
}
2626
}
2727

28-
/// Initalizes a string slice with the given string data and bounds
28+
/// Initializes a string slice with the given string data and bounds
2929
///
3030
/// # Safety
3131
/// Care must be taken to ensure that the bounds are valid within the provided string,
32-
/// i.e. string.get(bounds).is_some() must be true.
32+
/// i.e. `string.get(bounds).is_some()` must be true.
3333
pub unsafe fn new_unchecked(string: Ptr<str>, bounds: Range<usize>) -> Self {
3434
Self {
3535
data: string,
@@ -98,9 +98,17 @@ impl From<String> for StringSlice {
9898
}
9999
}
100100

101+
impl Deref for StringSlice {
102+
type Target = str;
103+
104+
fn deref(&self) -> &str {
105+
self.as_str()
106+
}
107+
}
108+
101109
impl AsRef<str> for StringSlice {
102110
fn as_ref(&self) -> &str {
103-
self.as_str()
111+
self.deref()
104112
}
105113
}
106114

crates/runtime/src/io/stdio.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub struct DefaultStdin {}
77

88
impl KotoFile for DefaultStdin {
99
fn id(&self) -> KString {
10-
STDIN_ID.with(|id| id.clone())
10+
"_stdin_".into()
1111
}
1212
}
1313

@@ -35,7 +35,7 @@ pub struct DefaultStdout {}
3535

3636
impl KotoFile for DefaultStdout {
3737
fn id(&self) -> KString {
38-
STDOUT_ID.with(|id| id.clone())
38+
"_stdout_".into()
3939
}
4040
}
4141

@@ -63,7 +63,7 @@ pub struct DefaultStderr {}
6363

6464
impl KotoFile for DefaultStderr {
6565
fn id(&self) -> KString {
66-
STDERR_ID.with(|id| id.clone())
66+
"_stderr_".into()
6767
}
6868
}
6969

@@ -84,9 +84,3 @@ impl KotoWrite for DefaultStderr {
8484
io::stdout().flush().map_err(map_io_err)
8585
}
8686
}
87-
88-
thread_local! {
89-
static STDIN_ID: KString = "_stdin_".into();
90-
static STDOUT_ID: KString = "_stdout_".into();
91-
static STDERR_ID: KString = "_stderr_".into();
92-
}

0 commit comments

Comments
 (0)