godot_core/builtin/
rid.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
 * Copyright (c) godot-rust; Bromeon and contributors.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

use std::num::NonZeroU64;

use godot_ffi as sys;
use sys::{ffi_methods, static_assert, static_assert_eq_size_align, GodotFfi};

/// A RID ("resource ID") is an opaque handle that refers to a Godot `Resource`.
///
/// RIDs do not grant access to the resource itself. Instead, they can be used in lower-level resource APIs
/// such as the [servers]. See also [Godot API docs for `RID`][docs].
///
/// RIDs should be largely safe to work with. Certain calls to servers may fail, however doing so will
/// trigger an error from Godot, and will not cause any UB.
///
/// # Safety caveat
///
/// In Godot 3, `RID` was not as safe as described here. We believe that this is fixed in Godot 4, but this has
/// not been extensively tested as of yet. Some confirmed UB from Godot 3 does not occur anymore, but if you
/// find anything suspicious or outright UB please open an issue.
///
/// [servers]: https://docs.godotengine.org/en/stable/tutorials/optimization/using_servers.html
/// [docs]: https://docs.godotengine.org/en/stable/classes/class_rid.html
///
/// # Godot docs
///
/// [`RID` (stable)](https://docs.godotengine.org/en/stable/classes/class_rid.html)

// Using normal Rust repr to take advantage of the nullable pointer optimization. As this enum is
// eligible for it, it is also guaranteed to have it. Meaning the layout of this type is identical to `u64`.
// See: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization
// Cannot use `#[repr(C)]` as it does not use the nullable pointer optimization.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Rid {
    /// A valid RID may refer to some resource, but is not guaranteed to do so.
    Valid(NonZeroU64),

    /// An invalid RID will never refer to a resource. Internally it is represented as a 0.
    Invalid,
}

// Ensure that `Rid`s actually have the layout we expect. Since `Rid` has the same size as `u64`, it cannot
// have any padding. As the `Valid` variant must take up all but one of the niches (since it contains a
// `NonZerou64`), and the `Invalid` variant must take up the final niche.
static_assert_eq_size_align!(Rid, u64);

// SAFETY:
// As Rid and u64 have the same size, and `Rid::Invalid` is initialized, it must be represented by some `u64`
// Therefore we can safely transmute it to u64.
//
// Ensure that `Rid::Invalid` actually is represented by 0, as it should be.
static_assert!(unsafe { std::mem::transmute::<Rid, u64>(Rid::Invalid) } == 0u64);

impl Rid {
    /// Create a new RID.
    #[inline]
    pub const fn new(id: u64) -> Self {
        match NonZeroU64::new(id) {
            Some(id) => Self::Valid(id),
            None => Self::Invalid,
        }
    }

    /// Convert this RID into a [`u64`]. Returns 0 if it is invalid.
    ///
    /// _Godot equivalent: `Rid.get_id()`_
    #[doc(alias = "get_id")]
    #[inline]
    pub const fn to_u64(self) -> u64 {
        match self {
            Rid::Valid(id) => id.get(),
            Rid::Invalid => 0,
        }
    }

    /// Convert this RID into a [`u64`] if it is valid. Otherwise, return None.
    #[inline]
    pub const fn to_valid_u64(self) -> Option<u64> {
        match self {
            Rid::Valid(id) => Some(id.get()),
            Rid::Invalid => None,
        }
    }

    /// Returns `true` if this is a valid RID.
    #[inline]
    pub const fn is_valid(&self) -> bool {
        matches!(self, Rid::Valid(_))
    }

    /// Returns `true` if this is an invalid RID.
    #[inline]
    pub const fn is_invalid(&self) -> bool {
        matches!(self, Rid::Invalid)
    }
}

impl std::fmt::Display for Rid {
    /// Formats `Rid` to match Godot's string representation.
    ///
    /// # Example
    /// ```
    /// use godot::prelude::*;
    /// let id = Rid::new(1);
    /// assert_eq!(format!("{}", id), "RID(1)");
    /// ```
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // godot output: `RID(0)`
        match self {
            Rid::Valid(x) => write!(f, "RID({})", x),
            Rid::Invalid => write!(f, "RID(0)"),
        }
    }
}

// SAFETY:
// This type is represented as `Self` in Godot, so `*mut Self` is sound.
unsafe impl GodotFfi for Rid {
    fn variant_type() -> sys::VariantType {
        sys::VariantType::RID
    }

    ffi_methods! { type sys::GDExtensionTypePtr = *mut Self;
        fn new_from_sys;
        fn new_with_uninit;
        fn from_arg_ptr;
        fn sys;
        fn sys_mut;
        fn move_return_ptr;
    }

    unsafe fn new_with_init(init: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
        let mut rid = Self::Invalid;
        init(rid.sys_mut());
        rid
    }
}

crate::meta::impl_godot_as_self!(Rid);