44 * License, v. 2.0. If a copy of the MPL was not distributed with this
55 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
66 */
7+
78use std:: fmt;
89
910use godot_ffi as sys;
10- use godot_ffi:: interface_fn;
1111use sys:: { ffi_methods, ExtVariantType , GodotFfi } ;
1212
1313use crate :: builtin:: { inner, Encoding , GString , NodePath , Variant } ;
@@ -33,11 +33,6 @@ use crate::{impl_shared_string_api, meta};
3333/// Note that Godot ignores any bytes after a null-byte. This means that for instance `"hello, world!"` and \
3434/// `"hello, world!\0 ignored by Godot"` will be treated as the same string if converted to a `StringName`.
3535///
36- /// # Performance
37- ///
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
39- /// [`From<&CStr>` impl](#impl-From<%26CStr>-for-StringName).
40- ///
4136/// # All string types
4237///
4338/// | Intended use case | String type |
@@ -88,7 +83,7 @@ impl StringName {
8883 let is_static = sys:: conv:: SYS_FALSE ;
8984 let s = unsafe {
9085 Self :: new_with_string_uninit ( |string_ptr| {
91- let ctor = interface_fn ! ( string_name_new_with_latin1_chars) ;
86+ let ctor = sys :: interface_fn!( string_name_new_with_latin1_chars) ;
9287 ctor (
9388 string_ptr,
9489 cstr. as_ptr ( ) as * const std:: ffi:: c_char ,
@@ -248,6 +243,48 @@ impl StringName {
248243 pub fn as_inner ( & self ) -> inner:: InnerStringName < ' _ > {
249244 inner:: InnerStringName :: from_outer ( self )
250245 }
246+
247+ #[ doc( hidden) ] // Private for now. Needs API discussion, also regarding overlap with try_from_cstr().
248+ pub fn __cstr ( c_str : & ' static std:: ffi:: CStr ) -> Self {
249+ // This used to be set to true, but `p_is_static` parameter in Godot should only be enabled if the result is indeed stored
250+ // in a static. See discussion in https://github.com/godot-rust/gdext/pull/1316. We may unify this into a regular constructor,
251+ // or provide a dedicated StringName cache (similar to ClassId cache) in the future, which would be freed on shutdown.
252+ let is_static = false ;
253+
254+ Self :: __cstr_with_static ( c_str, is_static)
255+ }
256+
257+ /// Creates a `StringName` from a static ASCII/Latin-1 `c"string"`.
258+ ///
259+ /// If `is_static` is true, avoids unnecessary copies and allocations and directly uses the backing buffer. However, this must
260+ /// be stored in an actual `static` to not cause leaks/error messages with Godot. For literals, use `is_static=false`.
261+ ///
262+ /// Note that while Latin-1 encoding is the most common encoding for c-strings, it isn't a requirement. So if your c-string
263+ /// uses a different encoding (e.g. UTF-8), it is possible that some characters will not show up as expected.
264+ ///
265+ /// # Safety
266+ /// `c_str` must be a static c-string that remains valid for the entire program duration.
267+ ///
268+ /// # Example
269+ /// ```no_run
270+ /// use godot::builtin::StringName;
271+ ///
272+ /// // '±' is a Latin-1 character with codepoint 0xB1. Note that this is not UTF-8, where it would need two bytes.
273+ /// let sname = StringName::__cstr(c"\xb1 Latin-1 string");
274+ /// ```
275+ #[ doc( hidden) ] // Private for now. Needs API discussion, also regarding overlap with try_from_cstr().
276+ pub fn __cstr_with_static ( c_str : & ' static std:: ffi:: CStr , is_static : bool ) -> Self {
277+ // SAFETY: c_str is nul-terminated and remains valid for entire program duration.
278+ unsafe {
279+ Self :: new_with_string_uninit ( |ptr| {
280+ sys:: interface_fn!( string_name_new_with_latin1_chars) (
281+ ptr,
282+ c_str. as_ptr ( ) ,
283+ sys:: conv:: bool_to_sys ( is_static) ,
284+ )
285+ } )
286+ }
287+ }
251288}
252289
253290// SAFETY:
@@ -354,35 +391,6 @@ impl From<&NodePath> for StringName {
354391 }
355392}
356393
357- impl From < & std:: ffi:: CStr > for StringName {
358- /// Creates a `StringName` from a ASCII/Latin-1 `c"string"`.
359- ///
360- /// This avoids unnecessary copies and allocations and directly uses the backing buffer. Useful for literals.
361- ///
362- /// Note that while Latin-1 encoding is the most common encoding for c-strings, it isn't a requirement. So if your c-string
363- /// uses a different encoding (e.g. UTF-8), it is possible that some characters will not show up as expected.
364- ///
365- /// # Example
366- /// ```no_run
367- /// use godot::builtin::StringName;
368- ///
369- /// // '±' is a Latin-1 character with codepoint 0xB1. Note that this is not UTF-8, where it would need two bytes.
370- /// let sname = StringName::from(c"\xb1 Latin-1 string");
371- /// ```
372- fn from ( c_str : & std:: ffi:: CStr ) -> Self {
373- // SAFETY: c_str is nul-terminated and remains valid for entire program duration.
374- unsafe {
375- Self :: new_with_string_uninit ( |ptr| {
376- sys:: interface_fn!( string_name_new_with_latin1_chars) (
377- ptr,
378- c_str. as_ptr ( ) ,
379- sys:: conv:: SYS_FALSE , // p_is_static
380- )
381- } )
382- }
383- }
384- }
385-
386394// ----------------------------------------------------------------------------------------------------------------------------------------------
387395// Ordering
388396
@@ -489,29 +497,18 @@ mod serialize {
489497 }
490498}
491499
500+ // TODO(v0.4.x): consider re-exposing in public API. Open questions: thread-safety, performance, memory leaks, global overhead.
501+ // Possibly in a more general StringName cache, similar to ClassId. See https://github.com/godot-rust/gdext/pull/1316.
492502/// Creates and gets a reference to a static `StringName` from a ASCII/Latin-1 `c"string"`.
493503///
494504/// 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.
495505#[ macro_export]
496- macro_rules! static_name {
506+ macro_rules! static_sname {
497507 ( $str: literal) => { {
498508 use std:: sync:: OnceLock ;
499509
500- use godot:: sys;
501-
502510 let c_str: & ' static std:: ffi:: CStr = $str;
503511 static SNAME : OnceLock <StringName > = OnceLock :: new( ) ;
504- SNAME . get_or_init( || {
505- // SAFETY: c_str is nul-terminated and remains valid for entire program duration.
506- unsafe {
507- StringName :: new_with_string_uninit( |ptr| {
508- sys:: interface_fn!( string_name_new_with_latin1_chars) (
509- ptr,
510- c_str. as_ptr( ) ,
511- sys:: conv:: SYS_TRUE , // p_is_static
512- )
513- } )
514- }
515- } )
512+ SNAME . get_or_init( || StringName :: __cstr_with_static( c_str, true ) )
516513 } } ;
517514}
0 commit comments