fory_core/resolver/
context.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::buffer::{Reader, Writer};
19use std::mem;
20
21use crate::error::Error;
22use crate::meta::MetaString;
23use crate::resolver::meta_resolver::{MetaReaderResolver, MetaWriterResolver};
24use crate::resolver::meta_string_resolver::{MetaStringReaderResolver, MetaStringWriterResolver};
25use crate::resolver::ref_resolver::{RefReader, RefWriter};
26use crate::resolver::type_resolver::{TypeInfo, TypeResolver};
27use crate::types;
28use crate::util::Spinlock;
29use std::rc::Rc;
30
31/// Serialization state container used on a single thread at a time.
32/// Sharing the same instance across threads simultaneously causes undefined behavior.
33#[allow(clippy::needless_lifetimes)]
34pub struct WriteContext<'a> {
35    // Replicated environment fields (direct access, no Arc indirection for flags)
36    type_resolver: TypeResolver,
37    compatible: bool,
38    share_meta: bool,
39    compress_string: bool,
40    xlang: bool,
41    check_struct_version: bool,
42
43    // Context-specific fields
44    default_writer: Option<Writer<'a>>,
45    pub writer: Writer<'a>,
46    meta_resolver: MetaWriterResolver,
47    meta_string_resolver: MetaStringWriterResolver,
48    pub ref_writer: RefWriter,
49}
50
51#[allow(clippy::needless_lifetimes)]
52impl<'a> WriteContext<'a> {
53    pub fn new(
54        type_resolver: TypeResolver,
55        compatible: bool,
56        share_meta: bool,
57        compress_string: bool,
58        xlang: bool,
59        check_struct_version: bool,
60    ) -> WriteContext<'a> {
61        WriteContext {
62            type_resolver,
63            compatible,
64            share_meta,
65            compress_string,
66            xlang,
67            check_struct_version,
68            default_writer: None,
69            writer: Writer::from_buffer(Self::get_leak_buffer()),
70            meta_resolver: MetaWriterResolver::default(),
71            meta_string_resolver: MetaStringWriterResolver::default(),
72            ref_writer: RefWriter::new(),
73        }
74    }
75
76    #[inline(always)]
77    fn get_leak_buffer() -> &'static mut Vec<u8> {
78        Box::leak(Box::new(vec![]))
79    }
80
81    #[inline(always)]
82    pub fn attach_writer(&mut self, writer: Writer<'a>) {
83        let old = mem::replace(&mut self.writer, writer);
84        self.default_writer = Some(old);
85    }
86
87    #[inline(always)]
88    pub fn detach_writer(&mut self) {
89        let default = mem::take(&mut self.default_writer);
90        self.writer = default.unwrap();
91    }
92
93    /// Get type resolver
94    #[inline(always)]
95    pub fn get_type_resolver(&self) -> &TypeResolver {
96        &self.type_resolver
97    }
98
99    #[inline(always)]
100    pub fn get_type_info(&self, type_id: &std::any::TypeId) -> Result<Rc<TypeInfo>, Error> {
101        self.type_resolver.get_type_info(type_id)
102    }
103
104    /// Check if compatible mode is enabled
105    #[inline(always)]
106    pub fn is_compatible(&self) -> bool {
107        self.compatible
108    }
109
110    /// Check if meta sharing is enabled
111    #[inline(always)]
112    pub fn is_share_meta(&self) -> bool {
113        self.share_meta
114    }
115
116    /// Check if string compression is enabled
117    #[inline(always)]
118    pub fn is_compress_string(&self) -> bool {
119        self.compress_string
120    }
121
122    /// Check if cross-language mode is enabled
123    #[inline(always)]
124    pub fn is_xlang(&self) -> bool {
125        self.xlang
126    }
127
128    /// Check if class version checking is enabled
129    #[inline(always)]
130    pub fn is_check_struct_version(&self) -> bool {
131        self.check_struct_version
132    }
133
134    #[inline(always)]
135    pub fn empty(&mut self) -> bool {
136        self.meta_resolver.empty()
137    }
138
139    #[inline(always)]
140    pub fn push_meta(&mut self, type_id: std::any::TypeId) -> Result<usize, Error> {
141        self.meta_resolver.push(type_id, &self.type_resolver)
142    }
143
144    #[inline(always)]
145    pub fn write_meta(&mut self, offset: usize) {
146        let len = self.writer.len();
147        self.writer
148            .set_bytes(offset, &((len - offset - 4) as u32).to_le_bytes());
149        self.meta_resolver.to_bytes(&mut self.writer);
150    }
151
152    pub fn write_any_typeinfo(
153        &mut self,
154        fory_type_id: u32,
155        concrete_type_id: std::any::TypeId,
156    ) -> Result<Rc<TypeInfo>, Error> {
157        if types::is_internal_type(fory_type_id) {
158            self.writer.write_varuint32(fory_type_id);
159            return self
160                .type_resolver
161                .get_type_info_by_id(fory_type_id)
162                .ok_or_else(|| Error::type_error("Type info for internal type not found"));
163        }
164        let type_info = self.type_resolver.get_type_info(&concrete_type_id)?;
165        let fory_type_id = type_info.get_type_id();
166        let namespace = type_info.get_namespace();
167        let type_name = type_info.get_type_name();
168        self.writer.write_varuint32(fory_type_id);
169        // should be compiled to jump table generation
170        match fory_type_id & 0xff {
171            types::NAMED_COMPATIBLE_STRUCT | types::COMPATIBLE_STRUCT => {
172                let meta_index =
173                    self.meta_resolver
174                        .push(concrete_type_id, &self.type_resolver)? as u32;
175                self.writer.write_varuint32(meta_index);
176            }
177            types::NAMED_ENUM | types::NAMED_EXT | types::NAMED_STRUCT => {
178                if self.is_share_meta() {
179                    let meta_index = self
180                        .meta_resolver
181                        .push(concrete_type_id, &self.type_resolver)?
182                        as u32;
183                    self.writer.write_varuint32(meta_index);
184                } else {
185                    self.write_meta_string_bytes(namespace)?;
186                    self.write_meta_string_bytes(type_name)?;
187                }
188            }
189            _ => {
190                // default case: do nothing
191            }
192        }
193        Ok(type_info)
194    }
195
196    #[inline(always)]
197    pub fn write_meta_string_bytes(&mut self, ms: Rc<MetaString>) -> Result<(), Error> {
198        self.meta_string_resolver
199            .write_meta_string_bytes(&mut self.writer, ms)
200    }
201
202    #[inline(always)]
203    pub fn reset(&mut self) {
204        self.meta_resolver.reset();
205        self.meta_string_resolver.reset();
206        self.ref_writer.reset();
207    }
208}
209
210#[allow(clippy::needless_lifetimes)]
211impl<'a> Drop for WriteContext<'a> {
212    fn drop(&mut self) {
213        unsafe {
214            drop(Box::from_raw(self.writer.bf));
215        }
216    }
217}
218
219// Safety: WriteContext is only shared across threads via higher-level pooling code that
220// ensures single-threaded access while the context is in use. Users must never hold the same
221// instance on multiple threads simultaneously; that would violate the invariants and result in
222// undefined behavior. Under that assumption, marking it Send/Sync is sound.
223#[allow(clippy::needless_lifetimes)]
224unsafe impl<'a> Send for WriteContext<'a> {}
225#[allow(clippy::needless_lifetimes)]
226unsafe impl<'a> Sync for WriteContext<'a> {}
227
228/// Deserialization state container used on a single thread at a time.
229/// Sharing the same instance across threads simultaneously causes undefined behavior.
230pub struct ReadContext<'a> {
231    // Replicated environment fields (direct access, no Arc indirection for flags)
232    type_resolver: TypeResolver,
233    compatible: bool,
234    share_meta: bool,
235    xlang: bool,
236    max_dyn_depth: u32,
237    check_struct_version: bool,
238
239    // Context-specific fields
240    pub reader: Reader<'a>,
241    pub meta_resolver: MetaReaderResolver,
242    meta_string_resolver: MetaStringReaderResolver,
243    pub ref_reader: RefReader,
244    current_depth: u32,
245}
246
247// Safety: ReadContext follows the same invariants as WriteContext—external orchestrators ensure
248// single-threaded use. Concurrent access to the same instance across threads is forbidden and
249// would result in undefined behavior. With exclusive use guaranteed, the Send/Sync markers are safe
250// even though Rc is used internally.
251#[allow(clippy::needless_lifetimes)]
252unsafe impl<'a> Send for ReadContext<'a> {}
253#[allow(clippy::needless_lifetimes)]
254unsafe impl<'a> Sync for ReadContext<'a> {}
255
256impl<'a> ReadContext<'a> {
257    pub fn new(
258        type_resolver: TypeResolver,
259        compatible: bool,
260        share_meta: bool,
261        xlang: bool,
262        max_dyn_depth: u32,
263        check_struct_version: bool,
264    ) -> ReadContext<'a> {
265        ReadContext {
266            type_resolver,
267            compatible,
268            share_meta,
269            xlang,
270            max_dyn_depth,
271            check_struct_version,
272            reader: Reader::default(),
273            meta_resolver: MetaReaderResolver::default(),
274            meta_string_resolver: MetaStringReaderResolver::default(),
275            ref_reader: RefReader::new(),
276            current_depth: 0,
277        }
278    }
279
280    /// Get type resolver
281    #[inline(always)]
282    pub fn get_type_resolver(&self) -> &TypeResolver {
283        &self.type_resolver
284    }
285
286    /// Check if compatible mode is enabled
287    #[inline(always)]
288    pub fn is_compatible(&self) -> bool {
289        self.compatible
290    }
291
292    /// Check if meta sharing is enabled
293    #[inline(always)]
294    pub fn is_share_meta(&self) -> bool {
295        self.share_meta
296    }
297
298    /// Check if cross-language mode is enabled
299    #[inline(always)]
300    pub fn is_xlang(&self) -> bool {
301        self.xlang
302    }
303
304    /// Check if class version checking is enabled
305    #[inline(always)]
306    pub fn is_check_struct_version(&self) -> bool {
307        self.check_struct_version
308    }
309
310    /// Get maximum dynamic depth
311    #[inline(always)]
312    pub fn max_dyn_depth(&self) -> u32 {
313        self.max_dyn_depth
314    }
315
316    #[inline(always)]
317    pub fn init(&mut self, max_dyn_depth: u32) {
318        self.max_dyn_depth = max_dyn_depth;
319        self.current_depth = 0;
320    }
321
322    #[inline(always)]
323    pub fn attach_reader(&mut self, reader: Reader<'a>) {
324        self.reader = reader;
325    }
326
327    #[inline(always)]
328    pub fn detach_reader(&mut self) -> Reader<'_> {
329        mem::take(&mut self.reader)
330    }
331
332    #[inline(always)]
333    pub fn get_type_info_by_index(&self, type_index: usize) -> Result<&Rc<TypeInfo>, Error> {
334        self.meta_resolver.get(type_index).ok_or_else(|| {
335            Error::type_error(format!("TypeInfo not found for type index: {}", type_index))
336        })
337    }
338
339    #[inline(always)]
340    pub fn load_type_meta(&mut self, offset: usize) -> Result<usize, Error> {
341        self.meta_resolver.load(
342            &self.type_resolver,
343            &mut Reader::new(&self.reader.slice_after_cursor()[offset..]),
344        )
345    }
346
347    pub fn read_any_typeinfo(&mut self) -> Result<Rc<TypeInfo>, Error> {
348        let fory_type_id = self.reader.read_varuint32()?;
349        // should be compiled to jump table generation
350        match fory_type_id & 0xff {
351            types::NAMED_COMPATIBLE_STRUCT | types::COMPATIBLE_STRUCT => {
352                let meta_index = self.reader.read_varuint32()? as usize;
353                let type_info = self.get_type_info_by_index(meta_index)?.clone();
354                Ok(type_info)
355            }
356            types::NAMED_ENUM | types::NAMED_EXT | types::NAMED_STRUCT => {
357                if self.is_share_meta() {
358                    let meta_index = self.reader.read_varuint32()? as usize;
359                    let type_info = self.get_type_info_by_index(meta_index)?.clone();
360                    Ok(type_info)
361                } else {
362                    let namespace = self.read_meta_string()?.to_owned();
363                    let type_name = self.read_meta_string()?.to_owned();
364                    let rc_namespace = Rc::from(namespace);
365                    let rc_type_name = Rc::from(type_name);
366                    self.type_resolver
367                        .get_type_info_by_meta_string_name(rc_namespace, rc_type_name)
368                        .ok_or_else(|| Error::type_error("Name harness not found"))
369                }
370            }
371            _ => self
372                .type_resolver
373                .get_type_info_by_id(fory_type_id)
374                .ok_or_else(|| Error::type_error("ID harness not found")),
375        }
376    }
377
378    #[inline(always)]
379    pub fn get_type_info(&self, type_id: &std::any::TypeId) -> Result<Rc<TypeInfo>, Error> {
380        self.type_resolver.get_type_info(type_id)
381    }
382
383    #[inline(always)]
384    pub fn read_meta_string(&mut self) -> Result<&MetaString, Error> {
385        self.meta_string_resolver.read_meta_string(&mut self.reader)
386    }
387
388    #[inline(always)]
389    pub fn inc_depth(&mut self) -> Result<(), Error> {
390        self.current_depth += 1;
391        if self.current_depth > self.max_dyn_depth() {
392            return Err(Error::depth_exceed(format!(
393                "Maximum dynamic object nesting depth ({}) exceeded. Current depth: {}. \
394                    This may indicate a circular reference or overly deep object graph. \
395                    Consider increasing max_dyn_depth if this is expected.",
396                self.max_dyn_depth(),
397                self.current_depth
398            )));
399        }
400        Ok(())
401    }
402
403    #[inline(always)]
404    pub fn dec_depth(&mut self) {
405        self.current_depth = self.current_depth.saturating_sub(1);
406    }
407
408    #[inline(always)]
409    pub fn reset(&mut self) {
410        self.meta_resolver.reset();
411        self.meta_string_resolver.reset();
412        self.ref_reader.reset();
413    }
414}
415
416pub struct Pool<T> {
417    items: Spinlock<Vec<T>>,
418    factory: Box<dyn Fn() -> T + Send + Sync>,
419}
420
421impl<T> Pool<T> {
422    pub fn new<F>(factory: F) -> Self
423    where
424        F: Fn() -> T + Send + Sync + 'static,
425    {
426        Pool {
427            items: Spinlock::new(vec![]),
428            factory: Box::new(factory),
429        }
430    }
431
432    #[inline(always)]
433    pub fn borrow_mut<Result>(&self, handler: impl FnOnce(&mut T) -> Result) -> Result {
434        let mut obj = self.get();
435        let result = handler(&mut obj);
436        self.put(obj);
437        result
438    }
439
440    #[inline(always)]
441    fn get(&self) -> T {
442        self.items.lock().pop().unwrap_or_else(|| (self.factory)())
443    }
444
445    // put back manually
446    #[inline(always)]
447    fn put(&self, item: T) {
448        self.items.lock().push(item);
449    }
450}