Skip to main content

fory_core/serializer/
array.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::error::Error;
19use crate::resolver::context::ReadContext;
20use crate::resolver::context::WriteContext;
21use crate::resolver::type_resolver::TypeResolver;
22use crate::serializer::primitive_list;
23use crate::serializer::{ForyDefault, Serializer};
24use crate::types::TypeId;
25use std::mem;
26use std::mem::MaybeUninit;
27
28use super::collection::{
29    read_collection_type_info, write_collection_data, write_collection_type_info,
30    DECL_ELEMENT_TYPE, HAS_NULL, IS_SAME_TYPE,
31};
32use super::list::{get_primitive_type_id, is_primitive_type};
33use crate::ensure;
34use crate::types::{RefFlag, RefMode};
35
36// Collection header flags (matching collection.rs private constants)
37const TRACKING_REF: u8 = 0b1;
38
39/// Validates that the deserialized length matches the expected array size N.
40#[inline(always)]
41fn validate_array_length(actual: usize, expected: usize) -> Result<(), Error> {
42    if actual != expected {
43        return Err(Error::invalid_data(format!(
44            "Array length mismatch: expected {}, got {}",
45            expected, actual
46        )));
47    }
48    Ok(())
49}
50
51/// Converts initialized MaybeUninit array to a regular array.
52/// # Safety
53/// All elements in the array must be initialized.
54#[inline(always)]
55unsafe fn assume_array_init<T, const N: usize>(arr: &[std::mem::MaybeUninit<T>; N]) -> [T; N] {
56    std::ptr::read(arr as *const _ as *const [T; N])
57}
58
59/// Read primitive array directly without intermediate Vec allocation
60#[inline]
61fn read_primitive_array<T, const N: usize>(context: &mut ReadContext) -> Result<[T; N], Error>
62where
63    T: Serializer + ForyDefault,
64{
65    // Read the size in bytes
66    let size_bytes = context.reader.read_varuint32()? as usize;
67    let elem_size = mem::size_of::<T>();
68    if size_bytes % elem_size != 0 {
69        return Err(Error::invalid_data("Invalid data length"));
70    }
71    let len = size_bytes / elem_size;
72    validate_array_length(len, N)?;
73    // Handle zero-sized arrays
74    if N == 0 {
75        // Safe: std::mem::zeroed() is explicitly safe for zero-sized types
76        return Ok(unsafe { std::mem::zeroed() });
77    }
78    // Create uninitialized array
79    let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
80    // Read bytes directly into array memory
81    unsafe {
82        let dst_ptr = arr.as_mut_ptr() as *mut u8;
83        let src = context.reader.read_bytes(size_bytes)?;
84        std::ptr::copy_nonoverlapping(src.as_ptr(), dst_ptr, size_bytes);
85    }
86    // Safety: all elements are now initialized with data from the reader
87    Ok(unsafe { assume_array_init(&arr) })
88}
89
90/// Read complex (non-primitive) array directly without intermediate Vec allocation
91#[inline]
92fn read_complex_array<T, const N: usize>(context: &mut ReadContext) -> Result<[T; N], Error>
93where
94    T: Serializer + ForyDefault,
95{
96    // Read collection length
97    let len = context.reader.read_varuint32()? as usize;
98    validate_array_length(len, N)?;
99    // Handle zero-sized arrays
100    if N == 0 {
101        // Safe: std::mem::zeroed() is explicitly safe for zero-sized types
102        return Ok(unsafe { std::mem::zeroed() });
103    }
104    // Handle polymorphic or shared ref types - need to use collection logic
105    if T::fory_is_polymorphic() || T::fory_is_shared_ref() {
106        return read_complex_array_dyn_ref(context, len);
107    }
108    // Read header
109    let header = context.reader.read_u8()?;
110    let declared = (header & DECL_ELEMENT_TYPE) != 0;
111    if !declared {
112        T::fory_read_type_info(context)?;
113    }
114    let has_null = (header & HAS_NULL) != 0;
115    ensure!(
116        (header & IS_SAME_TYPE) != 0,
117        Error::type_error("Type inconsistent, target type is not polymorphic")
118    );
119    // Create uninitialized array
120    let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
121    // Read elements directly into array
122    if !has_null {
123        for elem_slot in &mut arr[..] {
124            let elem = T::fory_read_data(context)?;
125            elem_slot.write(elem);
126        }
127    } else {
128        for elem_slot in &mut arr[..] {
129            let flag = context.reader.read_i8()?;
130            let elem = if flag == RefFlag::Null as i8 {
131                T::fory_default()
132            } else {
133                T::fory_read_data(context)?
134            };
135            elem_slot.write(elem);
136        }
137    }
138    // Safety: all elements are now initialized
139    Ok(unsafe { std::ptr::read(&arr as *const _ as *const [T; N]) })
140}
141
142/// Read complex array with dynamic/polymorphic types
143#[inline]
144fn read_complex_array_dyn_ref<T, const N: usize>(
145    context: &mut ReadContext,
146    len: usize,
147) -> Result<[T; N], Error>
148where
149    T: Serializer + ForyDefault,
150{
151    // Read header
152    let header = context.reader.read_u8()?;
153    let is_track_ref = (header & TRACKING_REF) != 0;
154    let is_same_type = (header & IS_SAME_TYPE) != 0;
155    let has_null = (header & HAS_NULL) != 0;
156    let is_declared = (header & DECL_ELEMENT_TYPE) != 0;
157    // Create uninitialized array
158    let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
159    // Read elements
160    if is_same_type {
161        let type_info = if !is_declared {
162            context.read_any_type_info()?
163        } else {
164            let rs_type_id = std::any::TypeId::of::<T>();
165            context.get_type_resolver().get_type_info(&rs_type_id)?
166        };
167        if is_track_ref {
168            for elem_slot in arr.iter_mut().take(len) {
169                let elem =
170                    T::fory_read_with_type_info(context, RefMode::Tracking, type_info.clone())?;
171                elem_slot.write(elem);
172            }
173        } else if !has_null {
174            for elem_slot in arr.iter_mut().take(len) {
175                let elem = T::fory_read_with_type_info(context, RefMode::None, type_info.clone())?;
176                elem_slot.write(elem);
177            }
178        } else {
179            for elem_slot in arr.iter_mut().take(len) {
180                let flag = context.reader.read_i8()?;
181                let elem = if flag == RefFlag::Null as i8 {
182                    T::fory_default()
183                } else {
184                    T::fory_read_with_type_info(context, RefMode::None, type_info.clone())?
185                };
186                elem_slot.write(elem);
187            }
188        }
189    } else {
190        // Match write side logic: Tracking > NullOnly (if has null) > None
191        let ref_mode = if is_track_ref {
192            RefMode::Tracking
193        } else if has_null {
194            RefMode::NullOnly
195        } else {
196            RefMode::None
197        };
198        for elem_slot in arr.iter_mut().take(len) {
199            let elem = T::fory_read(context, ref_mode, true)?;
200            elem_slot.write(elem);
201        }
202    }
203    // Safety: all elements are now initialized
204    Ok(unsafe { std::ptr::read(&arr as *const _ as *const [T; N]) })
205}
206
207// Implement Serializer for fixed-size arrays [T; N] where N is a const generic parameter
208impl<T: Serializer + ForyDefault, const N: usize> Serializer for [T; N] {
209    #[inline(always)]
210    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
211        if is_primitive_type::<T>() {
212            primitive_list::fory_write_data(self.as_slice(), context)
213        } else {
214            write_collection_data(self.iter(), context, false)
215        }
216    }
217
218    #[inline(always)]
219    fn fory_write_data_generic(
220        &self,
221        context: &mut WriteContext,
222        has_generics: bool,
223    ) -> Result<(), Error> {
224        if is_primitive_type::<T>() {
225            primitive_list::fory_write_data(self.as_slice(), context)
226        } else {
227            write_collection_data(self.iter(), context, has_generics)
228        }
229    }
230
231    #[inline(always)]
232    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
233        let id = get_primitive_type_id::<T>();
234        if id != TypeId::UNKNOWN {
235            primitive_list::fory_write_type_info(context, id)
236        } else {
237            write_collection_type_info(context, TypeId::LIST as u32)
238        }
239    }
240
241    #[inline(always)]
242    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
243        if is_primitive_type::<T>() {
244            // Read primitive array data directly without intermediate Vec allocation
245            read_primitive_array(context)
246        } else {
247            // Read collection data directly into array without intermediate Vec
248            read_complex_array(context)
249        }
250    }
251
252    #[inline(always)]
253    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
254        let id = get_primitive_type_id::<T>();
255        if id != TypeId::UNKNOWN {
256            primitive_list::fory_read_type_info(context, id)
257        } else {
258            read_collection_type_info(context, TypeId::LIST as u32)
259        }
260    }
261
262    #[inline(always)]
263    fn fory_reserved_space() -> usize {
264        if is_primitive_type::<T>() {
265            primitive_list::fory_reserved_space::<T>()
266        } else {
267            // size of the array length
268            mem::size_of::<u32>()
269        }
270    }
271
272    #[inline(always)]
273    fn fory_static_type_id() -> TypeId
274    where
275        Self: Sized,
276    {
277        let id = get_primitive_type_id::<T>();
278        if id != TypeId::UNKNOWN {
279            id
280        } else {
281            TypeId::LIST
282        }
283    }
284
285    #[inline(always)]
286    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
287        Ok(Self::fory_static_type_id())
288    }
289
290    #[inline(always)]
291    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
292        Ok(Self::fory_static_type_id())
293    }
294
295    #[inline(always)]
296    fn as_any(&self) -> &dyn std::any::Any {
297        self
298    }
299}
300
301impl<T, const N: usize> ForyDefault for [T; N]
302where
303    T: ForyDefault,
304{
305    #[inline(always)]
306    fn fory_default() -> Self {
307        // Create an array by calling fory_default() for each element
308        // We use MaybeUninit for safe initialization
309
310        let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
311        for elem in &mut arr {
312            elem.write(T::fory_default());
313        }
314
315        // Safety: all elements are initialized
316        unsafe {
317            // Transmute from [MaybeUninit<T>; N] to [T; N]
318            std::ptr::read(&arr as *const _ as *const [T; N])
319        }
320    }
321}