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