@@ -7,7 +7,18 @@ use crate::world::World;
7
7
/// [`Commands`] `T` type. Also this only reads the data via `read_unaligned` so unaligned
8
8
/// accesses are safe.
9
9
unsafe fn invoke_command < T : Command > ( command : * mut u8 , world : & mut World ) {
10
- let command = command. cast :: < T > ( ) . read_unaligned ( ) ;
10
+ let command = if std:: mem:: size_of :: < T > ( ) > 0 {
11
+ command. cast :: < T > ( ) . read_unaligned ( )
12
+ } else {
13
+ // NOTE: This is necessary because if the `CommandQueueInner` is only filled with 0-sized
14
+ // commands the `bytes` vec will never allocate. Then means that `bytes.as_ptr()` could be null
15
+ // and reading a null pointer is always UB.
16
+ // However according to https://doc.rust-lang.org/std/ptr/index.html
17
+ // "The canonical way to obtain a pointer that is valid for zero-sized accesses is NonNull::dangling"
18
+ // therefore the below code is safe to do.
19
+ let ptr = std:: ptr:: NonNull :: < T > :: dangling ( ) . as_ptr ( ) ;
20
+ ptr. cast :: < T > ( ) . read ( )
21
+ } ;
11
22
command. write ( world) ;
12
23
}
13
24
@@ -43,12 +54,9 @@ impl CommandQueueInner {
43
54
func : invoke_command :: < C > ,
44
55
} ) ;
45
56
46
- // Even if `size` == 0, we still need the vector to allocate.
47
- // When we call `read_unaliged` in `invoke_command`, the ptr must be non-null
48
- // therefore, `self.bytes.as_ptr()` must be non-null.
49
- self . bytes . reserve ( size. max ( 1 ) ) ;
50
-
51
57
if size > 0 {
58
+ self . bytes . reserve ( size) ;
59
+
52
60
// SAFE: The internal `bytes` vector has enough storage for the
53
61
// command (see the call the `reserve` above), and the vector has
54
62
// its length set appropriately.
0 commit comments