Skip to main content

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