@@ -9,7 +9,6 @@ use std::any::TypeId;
9
9
use std:: borrow:: Cow ;
10
10
use std:: cell:: OnceCell ;
11
11
use std:: collections:: HashMap ;
12
- use std:: ffi:: CStr ;
13
12
use std:: fmt;
14
13
use std:: hash:: Hash ;
15
14
@@ -95,7 +94,7 @@ impl ClassId {
95
94
"In Godot < 4.4, class name must be ASCII: '{name}'"
96
95
) ;
97
96
98
- cache. insert_class_id ( ClassIdSource :: Owned ( name) , Some ( type_id) , false )
97
+ cache. insert_class_id ( Cow :: Owned ( name) , Some ( type_id) , false )
99
98
}
100
99
101
100
/// Create a `ClassId` from a runtime string (for dynamic class names).
@@ -105,7 +104,7 @@ impl ClassId {
105
104
pub ( crate ) fn new_dynamic ( class_name : String ) -> Self {
106
105
let mut cache = CLASS_ID_CACHE . lock ( ) ;
107
106
108
- cache. insert_class_id ( ClassIdSource :: Owned ( class_name) , None , false )
107
+ cache. insert_class_id ( Cow :: Owned ( class_name) , None , false )
109
108
}
110
109
111
110
// Test-only APIs.
@@ -127,37 +126,16 @@ impl ClassId {
127
126
Self { global_index : 0 }
128
127
}
129
128
130
- /// Create a new ASCII; expect to be unique. Internal, reserved for macros.
131
- #[ doc( hidden) ]
132
- pub fn __alloc_next_ascii ( class_name_cstr : & ' static CStr ) -> Self {
133
- let utf8 = class_name_cstr
134
- . to_str ( )
135
- . expect ( "class name is invalid UTF-8" ) ;
136
-
137
- assert ! (
138
- utf8. is_ascii( ) ,
139
- "ClassId::alloc_next_ascii() with non-ASCII Unicode string '{utf8}'"
140
- ) ;
141
-
142
- let source = ClassIdSource :: Borrowed ( class_name_cstr) ;
143
- let mut cache = CLASS_ID_CACHE . lock ( ) ;
144
- cache. insert_class_id ( source, None , true )
145
- }
146
-
147
129
/// Create a new Unicode entry; expect to be unique. Internal, reserved for macros.
148
130
#[ doc( hidden) ]
149
131
pub fn __alloc_next_unicode ( class_name_str : & ' static str ) -> Self {
132
+ #[ cfg( before_api = "4.4" ) ]
150
133
assert ! (
151
- cfg! ( since_api = "4.4" ) ,
134
+ class_name_str . is_ascii ( ) ,
152
135
"Before Godot 4.4, class names must be ASCII, but '{class_name_str}' is not.\n See https://github.com/godotengine/godot/pull/96501."
153
136
) ;
154
137
155
- assert ! (
156
- !class_name_str. is_ascii( ) ,
157
- "ClassId::__alloc_next_unicode() with ASCII string '{class_name_str}'"
158
- ) ;
159
-
160
- let source = ClassIdSource :: Owned ( class_name_str. to_owned ( ) ) ;
138
+ let source = Cow :: Borrowed ( class_name_str) ;
161
139
let mut cache = CLASS_ID_CACHE . lock ( ) ;
162
140
cache. insert_class_id ( source, None , true )
163
141
}
@@ -181,7 +159,7 @@ impl ClassId {
181
159
pub fn to_cow_str ( & self ) -> Cow < ' static , str > {
182
160
let cache = CLASS_ID_CACHE . lock ( ) ;
183
161
let entry = cache. get_entry ( self . global_index as usize ) ;
184
- entry. rust_str . as_cow_str ( )
162
+ entry. rust_str . clone ( )
185
163
}
186
164
187
165
/// The returned pointer is valid indefinitely, as entries are never deleted from the cache.
@@ -198,23 +176,21 @@ impl ClassId {
198
176
199
177
let string_name = entry
200
178
. godot_str
201
- . get_or_init ( || entry. rust_str . to_string_name ( ) ) ;
179
+ . get_or_init ( || StringName :: from ( entry. rust_str . as_ref ( ) ) ) ;
202
180
203
181
func ( string_name)
204
182
}
205
183
}
206
184
207
185
impl fmt:: Display for ClassId {
208
186
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
209
- self . with_string_name ( |s| s . fmt ( f) )
187
+ self . to_cow_str ( ) . fmt ( f)
210
188
}
211
189
}
212
190
213
191
impl fmt:: Debug for ClassId {
214
192
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
215
- let cache = CLASS_ID_CACHE . lock ( ) ;
216
- let entry = cache. get_entry ( self . global_index as usize ) ;
217
- let name = entry. rust_str . as_cow_str ( ) ;
193
+ let name = self . to_cow_str ( ) ;
218
194
219
195
if name. is_empty ( ) {
220
196
write ! ( f, "ClassId(none)" )
@@ -224,58 +200,31 @@ impl fmt::Debug for ClassId {
224
200
}
225
201
}
226
202
227
- fn ascii_cstr_to_str ( cstr : & CStr ) -> & str {
228
- cstr. to_str ( ) . expect ( "should be validated ASCII" )
229
- }
230
-
231
203
// ----------------------------------------------------------------------------------------------------------------------------------------------
232
204
233
205
/// Entry in the class name cache.
234
206
///
235
207
/// `StringName` needs to be lazy-initialized because the Godot binding may not be initialized yet.
236
208
struct ClassIdEntry {
237
- rust_str : ClassIdSource ,
209
+ rust_str : Cow < ' static , str > ,
238
210
godot_str : OnceCell < StringName > ,
239
211
}
240
212
241
213
impl ClassIdEntry {
242
- fn new ( rust_str : ClassIdSource ) -> Self {
214
+ fn new ( rust_str : Cow < ' static , str > ) -> Self {
243
215
Self {
244
216
rust_str,
245
217
godot_str : OnceCell :: new ( ) ,
246
218
}
247
219
}
248
220
249
221
fn none ( ) -> Self {
250
- Self :: new ( ClassIdSource :: Borrowed ( c "") )
222
+ Self :: new ( Cow :: Borrowed ( "" ) )
251
223
}
252
224
}
253
225
254
226
// ----------------------------------------------------------------------------------------------------------------------------------------------
255
227
256
- /// `Cow`-like enum for class names, but with C strings as the borrowed variant.
257
- enum ClassIdSource {
258
- Owned ( String ) ,
259
- Borrowed ( & ' static CStr ) ,
260
- }
261
-
262
- impl ClassIdSource {
263
- fn to_string_name ( & self ) -> StringName {
264
- match self {
265
- ClassIdSource :: Owned ( s) => StringName :: from ( s) ,
266
- ClassIdSource :: Borrowed ( cstr) => StringName :: __cstr ( cstr) ,
267
- }
268
- }
269
-
270
- fn as_cow_str ( & self ) -> Cow < ' static , str > {
271
- match self {
272
- ClassIdSource :: Owned ( s) => Cow :: Owned ( s. clone ( ) ) ,
273
- ClassIdSource :: Borrowed ( cstr) => Cow :: Borrowed ( ascii_cstr_to_str ( cstr) ) ,
274
- }
275
- }
276
- }
277
- // ----------------------------------------------------------------------------------------------------------------------------------------------
278
-
279
228
/// Unified cache for all class name data.
280
229
struct ClassIdCache {
281
230
/// All class name entries, with index representing [`ClassId::global_index`].
@@ -308,23 +257,21 @@ impl ClassIdCache {
308
257
/// If `expect_first` is true and the string is already present in the cache.
309
258
fn insert_class_id (
310
259
& mut self ,
311
- source : ClassIdSource ,
260
+ source : Cow < ' static , str > ,
312
261
type_id : Option < TypeId > ,
313
262
expect_first : bool ,
314
263
) -> ClassId {
315
- let name_str = source. as_cow_str ( ) ;
316
-
317
264
if expect_first {
318
265
// Debug verification that we're indeed the first to register this string.
319
266
#[ cfg( debug_assertions) ]
320
267
assert ! (
321
- !self . string_to_index. contains_key( name_str . as_ref( ) ) ,
268
+ !self . string_to_index. contains_key( source . as_ref( ) ) ,
322
269
"insert_class_name() called for already-existing string: {}" ,
323
- name_str
270
+ source
324
271
) ;
325
272
} else {
326
273
// Check string cache first (dynamic path may reuse existing entries).
327
- if let Some ( & existing_index) = self . string_to_index . get ( name_str . as_ref ( ) ) {
274
+ if let Some ( & existing_index) = self . string_to_index . get ( source . as_ref ( ) ) {
328
275
// Update type cache if we have a TypeId and it's not already cached (dynamic-then-static case).
329
276
// Note: if type_id is Some, we know it came from new_cached_inner after a failed TypeId lookup.
330
277
if let Some ( type_id) = type_id {
@@ -342,9 +289,9 @@ impl ClassIdCache {
342
289
panic ! ( "ClassId cache exceeded maximum capacity of 65536 entries" )
343
290
} ) ;
344
291
345
- self . entries . push ( ClassIdEntry :: new ( source) ) ;
292
+ self . entries . push ( ClassIdEntry :: new ( source. clone ( ) ) ) ;
346
293
self . string_to_index
347
- . insert ( name_str . into_owned ( ) , global_index) ;
294
+ . insert ( source . into_owned ( ) , global_index) ;
348
295
349
296
if let Some ( type_id) = type_id {
350
297
self . type_to_index . insert ( type_id, global_index) ;
0 commit comments