1
- use crate :: nativescript:: NativeClass ;
2
1
use std:: any:: TypeId ;
2
+ use std:: mem:: { align_of, size_of} ;
3
+
4
+ use indexmap:: IndexSet ;
5
+
6
+ use crate :: nativescript:: NativeClass ;
3
7
4
8
#[ derive( Copy , Clone , Eq , PartialEq , Hash , Debug ) ]
5
9
struct Tag {
@@ -18,94 +22,68 @@ impl Tag {
18
22
}
19
23
}
20
24
21
- #[ cfg( target_pointer_width = "32" ) ]
22
- pub ( crate ) use self :: boxed_type_tag:: * ;
25
+ /// Whether the type tag can be transmuted to `usize`. `true` if the layouts are compatible
26
+ /// on the platform, and `type_tag_fallback` is not enabled.
27
+ const USE_TRANSMUTE : bool = cfg ! ( not( feature = "type_tag_fallback" ) )
28
+ && size_of :: < Tag > ( ) == size_of :: < usize > ( )
29
+ && align_of :: < Tag > ( ) == align_of :: < usize > ( ) ;
23
30
24
- #[ cfg( target_pointer_width = "64" ) ]
25
- pub ( crate ) use self :: transmuted_type_tag:: * ;
31
+ /// Keep track of allocated type tags so they can be freed on cleanup. This should only be
32
+ /// accessed from one thread at a time.
33
+ static mut TAGS : Option < IndexSet < Tag , ahash:: RandomState > > = None ;
26
34
27
- /// Type tags implemented as boxed pointers. This is required for 32-bit targets because `size_t`
28
- /// is only 32-bits wide there, while `TypeId` is always 64-bit.
29
- #[ cfg( target_pointer_width = "32" ) ]
30
- mod boxed_type_tag {
31
- use super :: Tag ;
32
- use crate :: nativescript:: NativeClass ;
33
- use std:: boxed:: Box ;
35
+ /// Top bit of `usize`. Used to prevent producing null type tags which might have special
36
+ /// meaning assigned.
37
+ const MAGIC : usize = 1usize . rotate_right ( 1 ) ;
38
+ /// Rest of `usize`.
39
+ const MAGIC_MASK : usize = !MAGIC ;
34
40
35
- /// Keep track of allocated type tags so they can be freed on cleanup
36
- static mut TAGS : Option < Vec < * const Tag > > = None ;
41
+ /// Create a new type tag for type `T`. This should only be called from `InitHandle`.
42
+ #[ inline]
43
+ pub ( crate ) unsafe fn create < T > ( ) -> * const libc:: c_void
44
+ where
45
+ T : NativeClass ,
46
+ {
47
+ let tag = Tag :: of :: < T > ( ) ;
37
48
38
- /// Create a new type tag for type `T`. This should only be called from `InitHandle`.
39
- #[ inline]
40
- pub ( crate ) unsafe fn create < T > ( ) -> * const libc:: c_void
41
- where
42
- T : NativeClass ,
43
- {
49
+ if USE_TRANSMUTE {
50
+ // Safety: USE_TRANSMUTE is only true if layouts match
51
+ * ( & tag as * const Tag as * const * const libc:: c_void )
52
+ } else {
44
53
// Safety: InitHandle is not Send or Sync, so this will only be called from one thread
45
- let tags = TAGS . get_or_insert_with ( Vec :: new) ;
46
- let type_tag = Box :: into_raw ( Box :: new ( Tag :: of :: < T > ( ) ) ) ;
47
- tags. push ( type_tag) ;
48
- type_tag as * const libc:: c_void
49
- }
50
-
51
- /// Returns `true` if `tag` corresponds to type `T`. `tag` must be one returned by `create`.
52
- #[ inline]
53
- pub ( crate ) unsafe fn check < T > ( tag : * const libc:: c_void ) -> bool
54
- where
55
- T : NativeClass ,
56
- {
57
- Tag :: of :: < T > ( ) == * ( tag as * const Tag )
58
- }
59
-
60
- /// Perform any cleanup actions if required. Should only be called from
61
- /// `crate::cleanup_internal_state`. `create` and `check` shouldn't be called after this.
62
- #[ inline]
63
- pub ( crate ) unsafe fn cleanup ( ) {
64
- // Safety: By the time cleanup is called, create shouldn't be called again
65
- if let Some ( tags) = TAGS . take ( ) {
66
- for ptr in tags. into_iter ( ) {
67
- std:: mem:: drop ( Box :: from_raw ( ptr as * mut Tag ) )
68
- }
69
- }
54
+ let tags = TAGS . get_or_insert_with ( IndexSet :: default) ;
55
+ let ( idx, _) = tags. insert_full ( tag) ;
56
+ // So we don't produce nulls. We're just assuming that 2^31 types will be
57
+ // enough for everyone here.
58
+ ( idx | MAGIC ) as * const libc:: c_void
70
59
}
71
60
}
72
61
73
- /// Type tags implemented as transmutes. This is faster on 64-bit targets, and require no
74
- /// allocation, as `TypeId` is `Copy`, and fits in a `size_t` there. This may break in the
75
- /// (probably very unlikely) event that:
76
- ///
77
- /// - `TypeId`'s size changes (possible in Rust 1.x as `TypeId` is opaque).
78
- /// - `TypeId` loses `Copy` (only possible in Rust 2.0+).
79
- ///
80
- /// Both will be compile errors: `transmute` should fail if the sizes mismatch, and the wrapper
81
- /// type `Tag` derives `Copy`.
82
- #[ cfg( target_pointer_width = "64" ) ]
83
- mod transmuted_type_tag {
84
- use super :: Tag ;
85
- use crate :: nativescript:: NativeClass ;
86
-
87
- /// Create a new type tag for type `T`. This should only be called from `InitHandle`.
88
- #[ inline]
89
- pub ( crate ) unsafe fn create < T > ( ) -> * const libc:: c_void
90
- where
91
- T : NativeClass ,
92
- {
93
- std:: mem:: transmute :: < Tag , * const libc:: c_void > ( Tag :: of :: < T > ( ) )
94
- }
95
-
96
- /// Returns `true` if `tag` corresponds to type `T`. `tag` must be one returned by `create`.
97
- #[ inline]
98
- pub ( crate ) unsafe fn check < T > ( tag : * const libc:: c_void ) -> bool
99
- where
100
- T : NativeClass ,
101
- {
102
- Tag :: of :: < T > ( ) == std:: mem:: transmute :: < * const libc:: c_void , Tag > ( tag)
62
+ /// Returns `true` if `tag` corresponds to type `T`. `tag` must be one returned by `create`.
63
+ #[ inline]
64
+ pub ( crate ) unsafe fn check < T > ( tag : * const libc:: c_void ) -> bool
65
+ where
66
+ T : NativeClass ,
67
+ {
68
+ if USE_TRANSMUTE {
69
+ // Safety: USE_TRANSMUTE is only true if layouts match
70
+ Tag :: of :: < T > ( ) == * ( & tag as * const * const libc:: c_void as * const Tag )
71
+ } else {
72
+ let tags = TAGS . as_ref ( ) . expect ( "tag should be created by `create`" ) ;
73
+ let idx = tag as usize ;
74
+ let tag = tags
75
+ . get_index ( idx & MAGIC_MASK )
76
+ . expect ( "tag should be created by `create`" ) ;
77
+ Tag :: of :: < T > ( ) == * tag
103
78
}
79
+ }
104
80
105
- /// Perform any cleanup actions if required. Should only be called from
106
- /// `crate::cleanup_internal_state`. `create` and `check` shouldn't be called after this.
107
- #[ inline]
108
- pub ( crate ) unsafe fn cleanup ( ) {
109
- // do nothing
81
+ /// Perform any cleanup actions if required. Should only be called from
82
+ /// `crate::cleanup_internal_state`. `create` and `check` shouldn't be called after this.
83
+ #[ inline]
84
+ pub ( crate ) unsafe fn cleanup ( ) {
85
+ // Safety: By the time cleanup is called, create shouldn't be called again
86
+ if let Some ( tags) = TAGS . take ( ) {
87
+ drop ( tags) ;
110
88
}
111
89
}
0 commit comments