Skip to main content

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