Skip to content

Commit 1ae588a

Browse files
committed
Add macro to create static StringName
1 parent c55eef0 commit 1ae588a

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

godot-core/src/builtin/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub mod __prelude_reexport {
4141
pub use rect2i::*;
4242
pub use rid::*;
4343
pub use signal::*;
44-
pub use string::{Encoding, GString, NodePath, StringName};
44+
pub use string::{static_name, Encoding, GString, NodePath, StringName};
4545
pub use transform2d::*;
4646
pub use transform3d::*;
4747
pub use variant::*;

godot-core/src/builtin/string/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub use string_name::*;
2222
use crate::meta;
2323
use crate::meta::error::ConvertError;
2424
use crate::meta::{FromGodot, GodotConvert, ToGodot};
25+
pub use crate::static_name;
2526

2627
impl GodotConvert for &str {
2728
type Via = GString;

godot-core/src/builtin/string/string_name.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::{impl_shared_string_api, meta};
3535
///
3636
/// # Performance
3737
///
38-
/// The fastest way to create string names is by using null-terminated C-string literals such as `c"MyClass"`. These can be used directly by Godot without conversion. The encoding is limited to Latin-1, however. See the corresponding
38+
/// The fastest way to create string names is using [`static_name!`][crate::builtin::static_name], which creates a static cached `StringName` from null-terminated C-string literals such as `c"MyClass"`. These can be used directly by Godot without conversion. The encoding is limited to Latin-1, however. See the corresponding
3939
/// [`From<&CStr>` impl](#impl-From<%26CStr>-for-StringName).
4040
///
4141
/// # All string types
@@ -512,3 +512,30 @@ mod serialize {
512512
}
513513
}
514514
}
515+
516+
/// Creates and gets a reference to a static `StringName` from a ASCII/Latin-1 `c"string"`.
517+
///
518+
/// This is the fastest way to create a StringName repeatedly, with the result being cached and never released, like `SNAME` in Godot source code. Suitable for scenarios where high performance is required.
519+
#[macro_export]
520+
macro_rules! static_name {
521+
($str:literal) => {{
522+
use std::sync::OnceLock;
523+
524+
use godot::sys;
525+
526+
let c_str: &'static std::ffi::CStr = $str;
527+
static SNAME: OnceLock<StringName> = OnceLock::new();
528+
SNAME.get_or_init(|| {
529+
// SAFETY: c_str is nul-terminated and remains valid for entire program duration.
530+
unsafe {
531+
StringName::new_with_string_uninit(|ptr| {
532+
sys::interface_fn!(string_name_new_with_latin1_chars)(
533+
ptr,
534+
c_str.as_ptr(),
535+
sys::conv::SYS_TRUE, // p_is_static
536+
)
537+
})
538+
}
539+
})
540+
}};
541+
}

itest/rust/src/builtin_tests/string/string_name_test.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use std::collections::HashSet;
99

10-
use godot::builtin::{Encoding, GString, NodePath, StringName};
10+
use godot::builtin::{static_name, Encoding, GString, NodePath, StringName};
1111

1212
use crate::framework::{assert_eq_self, itest};
1313

@@ -144,6 +144,30 @@ fn string_name_from_cstr() {
144144
}
145145
}
146146

147+
#[itest]
148+
fn string_name_static_name() {
149+
let a = static_name!(c"pure ASCII\t[~]").clone();
150+
let b = StringName::from("pure ASCII\t[~]");
151+
152+
assert_eq!(a, b);
153+
154+
let a1 = a.clone();
155+
let a2 = static_name!(c"pure ASCII\t[~]").clone();
156+
157+
assert_eq!(a, a1);
158+
assert_eq!(a1, a2);
159+
160+
let a = static_name!(c"\xB1").clone();
161+
let b = StringName::from("±");
162+
163+
assert_eq!(a, b);
164+
165+
let a = static_name!(c"Latin-1 \xA3 \xB1 text \xBE").clone();
166+
let b = StringName::from("Latin-1 £ ± text ¾");
167+
168+
assert_eq!(a, b);
169+
}
170+
147171
#[itest]
148172
fn string_name_with_null() {
149173
// Godot always ignores bytes after a null byte.

0 commit comments

Comments
 (0)