4
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
+
7
8
use std:: fmt;
8
9
9
10
use godot_ffi as sys;
10
- use godot_ffi:: interface_fn;
11
11
use sys:: { ffi_methods, ExtVariantType , GodotFfi } ;
12
12
13
13
use crate :: builtin:: { inner, Encoding , GString , NodePath , Variant } ;
@@ -33,11 +33,6 @@ use crate::{impl_shared_string_api, meta};
33
33
/// Note that Godot ignores any bytes after a null-byte. This means that for instance `"hello, world!"` and \
34
34
/// `"hello, world!\0 ignored by Godot"` will be treated as the same string if converted to a `StringName`.
35
35
///
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
- ///
41
36
/// # All string types
42
37
///
43
38
/// | Intended use case | String type |
@@ -88,7 +83,7 @@ impl StringName {
88
83
let is_static = sys:: conv:: SYS_FALSE ;
89
84
let s = unsafe {
90
85
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) ;
92
87
ctor (
93
88
string_ptr,
94
89
cstr. as_ptr ( ) as * const std:: ffi:: c_char ,
@@ -248,6 +243,48 @@ impl StringName {
248
243
pub fn as_inner ( & self ) -> inner:: InnerStringName < ' _ > {
249
244
inner:: InnerStringName :: from_outer ( self )
250
245
}
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
+ }
251
288
}
252
289
253
290
// SAFETY:
@@ -354,35 +391,6 @@ impl From<&NodePath> for StringName {
354
391
}
355
392
}
356
393
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
-
386
394
// ----------------------------------------------------------------------------------------------------------------------------------------------
387
395
// Ordering
388
396
@@ -489,29 +497,18 @@ mod serialize {
489
497
}
490
498
}
491
499
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.
492
502
/// Creates and gets a reference to a static `StringName` from a ASCII/Latin-1 `c"string"`.
493
503
///
494
504
/// 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.
495
505
#[ macro_export]
496
- macro_rules! static_name {
506
+ macro_rules! static_sname {
497
507
( $str: literal) => { {
498
508
use std:: sync:: OnceLock ;
499
509
500
- use godot:: sys;
501
-
502
510
let c_str: & ' static std:: ffi:: CStr = $str;
503
511
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 ) )
516
513
} } ;
517
514
}
0 commit comments