commonlibsse_ng\re\b/BSStringPool.rs
1use crate::re::BSAtomic::BSSpinLock;
2use core::{
3 ffi::c_char,
4 sync::atomic::{AtomicU16, Ordering},
5};
6
7#[derive(Debug)]
8#[repr(C)]
9pub struct Entry<T: StringFormat> {
10 /// Pointer to the previous entry in the list.
11 pub(crate) left: *mut Entry<T>,
12 /// Flags for entry status and reference count.
13 pub(crate) flags: AtomicU16,
14 /// CRC checksum for the entry.
15 pub(crate) crc: u16,
16 /// Union that holds either length or a pointer to the next entry.
17 pub(crate) length_or_right: LengthOrRight<T>,
18}
19const _: () = {
20 assert!(core::mem::offset_of!(Entry<U8>, left) == 0x0);
21 assert!(core::mem::offset_of!(Entry<U8>, flags) == 0x8);
22 assert!(core::mem::offset_of!(Entry<U8>, crc) == 0xa);
23 assert!(core::mem::offset_of!(Entry<U8>, length_or_right) == 0x10);
24 assert!(core::mem::size_of::<Entry<U8>>() == 0x18);
25};
26
27/// A union representing either the length of the entry or a pointer to the next entry.
28/// This allows for flexible storage of either data (length) or a pointer (right).
29#[repr(C)]
30pub union LengthOrRight<T: StringFormat> {
31 /// Holds the length of the entry.
32 length: u32,
33 /// Holds a pointer to the next entry.
34 right: *mut Entry<T>,
35}
36const _: [(); core::mem::size_of::<LengthOrRight<U8>>()] = [(); 0x8];
37
38impl<T> core::fmt::Debug for LengthOrRight<T>
39where
40 T: StringFormat,
41{
42 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43 unsafe {
44 f.debug_struct("LengthOrRight")
45 .field("length", &self.length)
46 .field("right", &self.right)
47 .finish()
48 }
49 }
50}
51
52impl<T: StringFormat> Entry<T> {
53 /// Constant to represent a "wide" entry. This is used for determining if the entry is wide.
54 pub const WIDE: u16 = 1 << 15;
55
56 /// Mask used for reference count in flags.
57 pub const REF_COUNT_MASK: u16 = 0x7FFF;
58
59 /// Mask used to extract the length value from the `length_or_right` union.
60 pub const LENGTH_MASK: u32 = 0xFFFFFF;
61
62 /// Acquire the entry by incrementing its reference count.
63 ///
64 /// This method ensures atomicity by loading the current value of the reference count, and then
65 /// attempts to increment the reference count. The operation is performed in a loop to ensure
66 /// that the reference count does not overflow.
67 pub fn acquire(&self) {
68 let flags = &self.flags;
69 let mut expected;
70 loop {
71 expected = flags.load(Ordering::Relaxed);
72 if (expected & Self::REF_COUNT_MASK) >= Self::REF_COUNT_MASK {
73 break;
74 }
75 if flags
76 .compare_exchange_weak(
77 expected,
78 expected.wrapping_add(1),
79 Ordering::AcqRel,
80 Ordering::Relaxed,
81 )
82 .is_ok()
83 {
84 break;
85 }
86 }
87 }
88
89 /// Get the CRC checksum of the entry.
90 #[inline]
91 pub const fn crc(&self) -> u16 {
92 self.crc
93 }
94
95 /// Returns the length of the entry, extracting it from the `length_or_right` union.
96 ///
97 /// This corresponds to the `length` or `size` function in the C++ API.
98 ///
99 /// # Returns
100 ///
101 /// Returns the length stored in the `length_or_right` union after applying the `LENGTH_MASK`.
102 #[allow(clippy::len_without_is_empty)]
103 #[inline]
104 pub const fn len(&self) -> u32 {
105 unsafe { self.length_or_right.length & Self::LENGTH_MASK }
106 }
107
108 /// Returns a raw pointer to the entry's data.
109 ///
110 /// This corresponds to the `data` function in the C++ API.
111 #[inline]
112 pub fn as_raw(&self) -> *const T::Unit {
113 T::is_valid(self.is_wide());
114 unsafe { (self as *const Self).add(1).cast::<T::Unit>() }
115 }
116
117 /// Releases the entry and performs the necessary cleanup. This function is unsafe because it
118 /// involves dereferencing raw pointers.
119 ///
120 /// # Safety
121 /// - The caller must ensure that the entry is properly allocated and not already released.
122 /// - Not having a double free.
123 #[inline]
124 pub unsafe fn release(entry: &*const T::Unit) {
125 unsafe { T::release(entry) };
126 }
127
128 /// Checks whether the entry is "wide" based on the flags.
129 #[inline]
130 fn is_wide(&self) -> bool {
131 (self.flags.load(Ordering::Relaxed) & Self::WIDE) != 0
132 }
133}
134
135/// The `StringFormat` trait defines the methods required to interact with a specific string format type.
136///
137/// This includes the type of unit (e.g., `u8` for `c_char` or `u16` for `w_char_t`) and methods for validation
138/// and releasing memory associated with entries.
139pub trait StringFormat {
140 /// U8/U16
141 type Unit;
142
143 /// Releases the entry associated with the string format type.
144 ///
145 /// # Safety
146 ///
147 /// - The caller must ensure the entry is valid and allocated.
148 /// - Not having a double free.
149 unsafe fn release(entry: &*const Self::Unit);
150
151 /// Validates the entry, ensuring the proper string format (e.g., UTF-8 or UTF-16).
152 ///
153 /// # Panics
154 ///
155 /// If the format does not match the expected type (wide or not), this function will panic.
156 fn is_valid(is_wide: bool);
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
160pub enum U8 {}
161
162impl StringFormat for U8 {
163 type Unit = c_char;
164
165 #[inline]
166 unsafe fn release(entry: &*const Self::Unit) {
167 unsafe { release8(entry) }
168 }
169
170 #[inline]
171 fn is_valid(is_wide: bool) {
172 assert!(!is_wide);
173 }
174}
175
176/// The `wchar_t` type is an implementation-defined wide character type.
177///
178/// In Microsoft compilers, it represents a 16-bit wide character used to store Unicode encoded as UTF-16LE.
179/// - ref: [`char、wchar_t、char8_t、char16_t、char32_t`](https://learn.microsoft.com/cpp/cpp/char-wchar-t-char16-t-char32-t?view=msvc-170)
180#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
181pub enum U16 {}
182
183impl StringFormat for U16 {
184 type Unit = u16;
185
186 #[inline]
187 unsafe fn release(entry: &*const Self::Unit) {
188 unsafe { release16(entry) }
189 }
190
191 #[inline]
192 fn is_valid(is_wide: bool) {
193 assert!(is_wide);
194 }
195}
196
197/// Releases a `c_char` entry. This is marked as `unsafe` because it operates directly on raw pointers.
198#[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67847, ae_id = 69192)]
199pub unsafe fn release8(entry: &*const c_char) {}
200
201/// Releases a `wchar_t` entry. This is marked as `unsafe` because it operates directly on raw pointers.
202#[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67848, ae_id = 69193)]
203pub unsafe fn release16(entry: &*const u16) {}
204
205#[repr(C)]
206pub struct BucketTable {
207 buckets: [*mut Entry<U8>; 0x10000], // 00000 - index using hash & kEntryIndexMask
208 locks: [BSSpinLock; 0x10000 / 0x800], // 80000 - index using hash & kLockIndexMask
209 initialized: bool, // 80100
210}
211const _: [(); core::mem::size_of::<BucketTable>()] = [(); 0x80108];
212
213impl BucketTable {
214 /// Returns the singleton instance of the `BucketTable`.
215 ///
216 /// This function is used to get a global instance for the table.
217 #[allow(clippy::use_self)]
218 #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67855, ae_id = 69200)]
219 pub fn get_singleton() -> *mut BucketTable {}
220}