il2cpp_bridge_rs/structs/collections/array.rs
1//! IL2CPP Array definition and operations
2use crate::api;
3use crate::structs::core::Class;
4use std::ffi::c_void;
5use std::marker::PhantomData;
6
7#[repr(C)]
8pub struct ArrayBounds {
9 /// Length of the dimension
10 pub length: usize,
11 /// Lower bound of the dimension
12 pub lower_bound: i32,
13}
14
15#[repr(C)]
16pub struct Il2cppArray<T> {
17 /// Pointer to the array class
18 pub klass: *mut c_void,
19 /// Monitor for synchronization
20 pub monitor: *mut c_void,
21 /// Pointer to array bounds
22 pub bounds: *mut ArrayBounds,
23 /// Maximum length of the array
24 pub max_length: usize,
25 phantom: PhantomData<T>,
26}
27
28impl<T: Copy> Il2cppArray<T> {
29 /// Gets the address of the data array
30 ///
31 /// # Returns
32 /// * `usize` - The memory address where the array data begins
33 pub fn get_data(&self) -> usize {
34 let header_size = std::mem::size_of::<Self>();
35 (self as *const Self as usize) + header_size
36 }
37
38 /// Gets the element at the specified index
39 ///
40 /// # Arguments
41 /// * `index` - The index of the element to retrieve
42 ///
43 /// # Returns
44 /// * `T` - The element at the specified index
45 ///
46 /// # Panics
47 /// Panics if the index is out of bounds.
48 pub fn get(&self, index: usize) -> T {
49 if index >= self.max_length {
50 panic!("Index out of bounds: {} >= {}", index, self.max_length);
51 }
52
53 unsafe {
54 let element_ptr = (self.get_data() + index * std::mem::size_of::<T>()) as *const T;
55 *element_ptr
56 }
57 }
58
59 /// Alias for `get`
60 ///
61 /// # Arguments
62 /// * `index` - The index of the element to retrieve
63 ///
64 /// # Returns
65 /// * `T` - The element at the specified index
66 pub fn at(&self, index: usize) -> T {
67 self.get(index)
68 }
69
70 /// Sets the element at the specified index
71 ///
72 /// # Arguments
73 /// * `index` - The index where the value should be set
74 /// * `value` - The value to set
75 ///
76 /// # Panics
77 /// Panics if the index is out of bounds.
78 pub fn set(&mut self, index: usize, value: T) {
79 if index >= self.max_length {
80 panic!("Index out of bounds: {} >= {}", index, self.max_length);
81 }
82
83 unsafe {
84 let element_ptr = (self.get_data() + index * std::mem::size_of::<T>()) as *mut T;
85 *element_ptr = value;
86 }
87 }
88
89 /// Gets a raw pointer to the data
90 ///
91 /// # Returns
92 /// * `*const T` - Pointer to the first element of the array
93 pub fn get_pointer(&self) -> *const T {
94 let header_size = std::mem::size_of::<Self>();
95 ((self as *const Self as usize) + header_size) as *const T
96 }
97
98 /// Inserts elements from a slice into the array
99 ///
100 /// # Arguments
101 /// * `arr` - The slice of elements to insert
102 /// * `size` - The number of elements to insert
103 /// * `index` - The starting index in the array
104 pub fn insert(&mut self, arr: &[T], size: usize, index: usize) {
105 if (size + index) >= self.max_length {
106 if index >= self.max_length {
107 return;
108 }
109
110 let new_size = self.max_length - index;
111 for (i, &item) in arr.iter().enumerate().take(new_size) {
112 self.set(i + index, item);
113 }
114 } else {
115 for (i, &item) in arr.iter().enumerate().take(size) {
116 self.set(i + index, item);
117 }
118 }
119 }
120
121 /// Fills the array with a value
122 ///
123 /// # Arguments
124 /// * `value` - The value to fill the array with
125 pub fn fill(&mut self, value: T) {
126 for i in 0..self.max_length {
127 self.set(i, value);
128 }
129 }
130
131 /// Removes an element at the specified index
132 ///
133 /// Shifts subsequent elements to the left in place.
134 ///
135 /// IL2CPP arrays are fixed-size, so this does not change `max_length`.
136 /// The final slot is left as a duplicate of the former tail element.
137 ///
138 /// # Arguments
139 /// * `index` - The index of the element to remove
140 pub fn remove_at(&mut self, index: usize) {
141 if index >= self.max_length {
142 return;
143 }
144
145 if index + 1 < self.max_length {
146 unsafe {
147 let data = self.get_pointer() as *mut T;
148 std::ptr::copy(
149 data.add(index + 1),
150 data.add(index),
151 self.max_length - index - 1,
152 );
153 }
154 }
155 }
156
157 /// Removes a range of elements
158 ///
159 /// Shifts subsequent elements to the left in place.
160 ///
161 /// IL2CPP arrays are fixed-size, so this does not change `max_length`.
162 /// Vacated tail slots are left duplicated from the previous tail values.
163 ///
164 /// # Arguments
165 /// * `index` - The starting index
166 /// * `count` - The number of elements to remove
167 pub fn remove_range(&mut self, index: usize, mut count: usize) {
168 if index >= self.max_length {
169 return;
170 }
171
172 if count == 0 {
173 count = 1;
174 }
175
176 let count = count.min(self.max_length - index);
177 let tail_len = self.max_length - index - count;
178
179 if tail_len > 0 {
180 unsafe {
181 let data = self.get_pointer() as *mut T;
182 std::ptr::copy(data.add(index + count), data.add(index), tail_len);
183 }
184 }
185 }
186
187 /// Removes all elements from the array
188 ///
189 /// IL2CPP arrays are fixed-size managed objects, so clearing them by
190 /// shrinking the header is unsupported. Overwrite elements explicitly if
191 /// you need sentinel values.
192 pub fn remove_all(&mut self) {}
193
194 /// Converts the array to a Rust Vec
195 ///
196 /// # Returns
197 /// * `Vec<T>` - A new vector containing the array elements
198 pub fn to_vector(&self) -> Vec<T> {
199 let mut result = Vec::with_capacity(self.max_length);
200 for i in 0..self.max_length {
201 result.push(self.at(i));
202 }
203 result
204 }
205
206 /// Creates a new array instance
207 ///
208 /// # Arguments
209 /// * `class` - The class of the elements
210 /// * `size` - The size of the array
211 ///
212 /// # Returns
213 /// * `*mut Self` - A pointer to the new array
214 pub fn new(class: &Class, size: usize) -> *mut Self {
215 unsafe { api::array_new(class.address, size as u32) as *mut Self }
216 }
217}