fory_core/fory.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 crate::context::{ContextCache, ReadContext, WriteContext};
21use crate::ensure;
22use crate::error::Error;
23use crate::resolver::RefMode;
24use crate::resolver::TypeResolver;
25use crate::serializer::ForyDefault;
26use crate::serializer::{Serializer, StructSerializer};
27use crate::type_id::config_flags::{IS_CROSS_LANGUAGE_FLAG, IS_OUT_OF_BAND_FLAG};
28use crate::type_id::SIZE_OF_REF_AND_TYPE;
29use std::cell::UnsafeCell;
30use std::mem;
31use std::sync::atomic::{AtomicU64, Ordering};
32use std::sync::OnceLock;
33
34/// Global counter to assign unique IDs to each Fory instance.
35static FORY_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
36
37thread_local! {
38 /// Thread-local storage for WriteContext instances with fast path caching.
39 static WRITE_CONTEXTS: UnsafeCell<ContextCache<WriteContext<'static>>> =
40 UnsafeCell::new(ContextCache::new());
41
42 /// Thread-local storage for ReadContext instances with fast path caching.
43 static READ_CONTEXTS: UnsafeCell<ContextCache<ReadContext<'static>>> =
44 UnsafeCell::new(ContextCache::new());
45}
46
47/// Builder for configuring a [`Fory`] instance before first use.
48///
49/// `ForyBuilder` owns the configuration phase. Call [`build`](Self::build) to create the
50/// instance, then use [`Fory`] for registration and serialization operations.
51///
52/// ```rust
53/// use fory_core::Fory;
54///
55/// let fory = Fory::builder()
56/// .compress_string(true)
57/// .max_dyn_depth(10)
58/// .build();
59/// ```
60#[derive(Default)]
61pub struct ForyBuilder {
62 config: Config,
63 compatible_set: bool,
64}
65
66impl ForyBuilder {
67 /// Sets the serialization compatible mode for this Fory builder.
68 ///
69 /// # Arguments
70 ///
71 /// * `compatible` - The serialization compatible mode to use. Options are:
72 /// - `false`: Every reader and writer must use the same schema.
73 /// Use only for smaller, faster same-schema payloads.
74 /// - `true`: Supports schema evolution and type metadata sharing for better
75 /// cross-version compatibility.
76 ///
77 /// # Returns
78 ///
79 /// Returns `self` for method chaining.
80 ///
81 /// # Note
82 ///
83 /// Setting the compatible mode also automatically configures the `share_meta` flag:
84 /// - `false` → `share_meta = false`
85 /// - `true` → `share_meta = true`
86 ///
87 /// # Examples
88 ///
89 /// ```rust
90 /// use fory_core::Fory;
91 ///
92 /// // Same-schema optimization.
93 /// let fory = Fory::builder().compatible(false).build();
94 /// ```
95 pub fn compatible(mut self, compatible: bool) -> Self {
96 self.compatible_set = true;
97 // Setting share_meta individually is not supported currently
98 self.config.share_meta = compatible;
99 self.config.compatible = compatible;
100 if compatible {
101 self.config.check_struct_version = false;
102 } else if self.config.xlang {
103 self.config.check_struct_version = true;
104 }
105 self
106 }
107
108 /// Enables or disables xlang mode.
109 ///
110 /// # Arguments
111 ///
112 /// * `xlang` - If `true`, uses the xlang wire format compatible with other Fory
113 /// implementations (Java, Python, C++, etc.). If `false`, uses Rust native mode.
114 ///
115 /// # Returns
116 ///
117 /// Returns `self` for method chaining.
118 ///
119 /// # Default
120 ///
121 /// The default value is `true`.
122 ///
123 /// # Examples
124 ///
125 /// ```rust
126 /// use fory_core::Fory;
127 ///
128 /// // Xlang mode, the default cross-language wire format
129 /// let fory = Fory::builder().xlang(true).build();
130 ///
131 /// // Native mode for Rust-only traffic
132 /// let fory = Fory::builder().xlang(false).build();
133 /// ```
134 pub fn xlang(mut self, xlang: bool) -> Self {
135 self.config.xlang = xlang;
136 if !self.compatible_set {
137 self.config.share_meta = true;
138 self.config.compatible = true;
139 self.config.check_struct_version = false;
140 return self;
141 }
142 if !self.config.check_struct_version {
143 self.config.check_struct_version = !self.config.compatible;
144 }
145 self
146 }
147
148 /// Enables or disables meta string compression.
149 ///
150 /// # Arguments
151 ///
152 /// * `compress_string` - If `true`, enables meta string compression to reduce serialized
153 /// payload size by deduplicating and encoding frequently used strings (such as type names
154 /// and field names). If `false`, strings are serialized without compression.
155 ///
156 /// # Returns
157 ///
158 /// Returns `self` for method chaining.
159 ///
160 /// # Default
161 ///
162 /// The default value is `false`.
163 ///
164 /// # Trade-offs
165 ///
166 /// - **Enabled**: Smaller payload size, slightly higher CPU overhead
167 /// - **Disabled**: Larger payload size, faster serialization/deserialization
168 ///
169 /// # Examples
170 ///
171 /// ```rust
172 /// use fory_core::Fory;
173 ///
174 /// let fory = Fory::builder().compress_string(true).build();
175 /// ```
176 pub fn compress_string(mut self, compress_string: bool) -> Self {
177 self.config.compress_string = compress_string;
178 self
179 }
180
181 /// Enables or disables checked UTF-8 string reads.
182 ///
183 /// Checked reads validate UTF-8 payload bytes before constructing Rust `String` values.
184 /// Disabling this keeps the faster unchecked construction path and must only be used when
185 /// serialized bytes are trusted to contain valid UTF-8 strings.
186 ///
187 /// # Default
188 ///
189 /// The default value is `true`.
190 pub fn check_string_read(mut self, check_string_read: bool) -> Self {
191 self.config.check_string_read = check_string_read;
192 self
193 }
194
195 /// Enables or disables schema hash checking for same-schema payloads.
196 ///
197 /// # Arguments
198 ///
199 /// * `check_struct_version` - If `true`, enables schema hash checking for same-schema
200 /// serialization and deserialization. When enabled,
201 /// a version hash computed from field types is written/read to detect schema mismatches.
202 /// If `false`, no version checking is performed.
203 ///
204 /// # Returns
205 ///
206 /// Returns `self` for method chaining.
207 ///
208 /// # Default
209 ///
210 /// The default value is `false`.
211 ///
212 /// # Note
213 ///
214 /// This feature is only effective when `compatible` mode is `false`. In compatible mode,
215 /// schema evolution is supported and version checking is not needed.
216 ///
217 /// # Examples
218 ///
219 /// ```rust
220 /// use fory_core::Fory;
221 ///
222 /// let fory = Fory::builder()
223 /// .compatible(false)
224 /// .check_struct_version(true)
225 /// .build();
226 /// ```
227 pub fn check_struct_version(mut self, check_struct_version: bool) -> Self {
228 if self.config.compatible && check_struct_version {
229 // ignore setting if compatible mode is on
230 return self;
231 }
232 self.config.check_struct_version = check_struct_version;
233 self
234 }
235
236 /// Enables or disables reference tracking for shared and circular references.
237 ///
238 /// # Arguments
239 ///
240 /// * `track_ref` - If `true`, enables reference tracking which allows
241 /// preserving shared object references and circular references during
242 /// serialization/deserialization.
243 ///
244 /// # Returns
245 ///
246 /// Returns `self` for method chaining.
247 ///
248 /// # Default
249 ///
250 /// The default value is `false`.
251 ///
252 /// # Examples
253 ///
254 /// ```rust
255 /// use fory_core::Fory;
256 ///
257 /// let fory = Fory::builder().track_ref(true).build();
258 /// ```
259 pub fn track_ref(mut self, track_ref: bool) -> Self {
260 self.config.track_ref = track_ref;
261 self
262 }
263
264 /// Sets the approximate graph-memory gate for one root deserialization.
265 ///
266 /// Mainly gates materialized collections, maps, arrays, structs, and objects. Leaf values are
267 /// gated by unread input bytes instead, and actual process memory can be higher. Defaults to
268 /// 128 MiB. Values must be positive byte limits.
269 pub fn max_graph_memory_bytes(mut self, max_bytes: usize) -> Self {
270 assert!(
271 max_bytes > 0,
272 "max_graph_memory_bytes must be in [1, usize::MAX]"
273 );
274 self.config.max_graph_memory_bytes = max_bytes;
275 self
276 }
277
278 /// Sets the maximum depth for nested dynamic object serialization.
279 ///
280 /// # Arguments
281 ///
282 /// * `max_dyn_depth` - The maximum nesting depth allowed for dynamically-typed objects
283 /// (e.g., trait objects, boxed types). This prevents stack overflow from deeply nested
284 /// structures in dynamic serialization scenarios.
285 ///
286 /// # Returns
287 ///
288 /// Returns `self` for method chaining.
289 ///
290 /// # Default
291 ///
292 /// The default value is `5`.
293 ///
294 /// # Behavior
295 ///
296 /// When the depth limit is exceeded during deserialization, an error is returned to prevent
297 /// potential stack overflow or infinite recursion.
298 ///
299 /// # Examples
300 ///
301 /// ```rust
302 /// use fory_core::Fory;
303 ///
304 /// // Allow deeper nesting for complex object graphs
305 /// let fory = Fory::builder().max_dyn_depth(10).build();
306 ///
307 /// // Restrict nesting for safer deserialization
308 /// let fory = Fory::builder().max_dyn_depth(3).build();
309 /// ```
310 pub fn max_dyn_depth(mut self, max_dyn_depth: u32) -> Self {
311 self.config.max_dyn_depth = max_dyn_depth;
312 self
313 }
314
315 /// Sets the maximum field count accepted in one received struct TypeMeta.
316 pub fn max_type_fields(mut self, max_fields: usize) -> Self {
317 assert!(max_fields > 0, "max_type_fields must be positive");
318 assert!(
319 u32::try_from(max_fields).is_ok(),
320 "max_type_fields is too large"
321 );
322 self.config.max_type_fields = max_fields as u32;
323 self
324 }
325
326 /// Sets the maximum body size accepted for one received TypeMeta.
327 pub fn max_type_meta_bytes(mut self, max_bytes: usize) -> Self {
328 assert!(max_bytes > 0, "max_type_meta_bytes must be positive");
329 assert!(
330 u32::try_from(max_bytes).is_ok(),
331 "max_type_meta_bytes is too large"
332 );
333 self.config.max_type_meta_bytes = max_bytes as u32;
334 self
335 }
336
337 /// Sets the maximum accepted remote metadata versions for one logical type.
338 pub fn max_schema_versions_per_type(mut self, max_versions: usize) -> Self {
339 assert!(
340 max_versions > 0,
341 "max_schema_versions_per_type must be positive"
342 );
343 assert!(
344 u32::try_from(max_versions).is_ok(),
345 "max_schema_versions_per_type is too large"
346 );
347 self.config.max_schema_versions_per_type = max_versions as u32;
348 self
349 }
350
351 /// Sets the maximum accepted average remote metadata versions across logical types.
352 pub fn max_average_schema_versions_per_type(mut self, max_versions: usize) -> Self {
353 assert!(
354 max_versions > 0,
355 "max_average_schema_versions_per_type must be positive"
356 );
357 assert!(
358 u32::try_from(max_versions).is_ok(),
359 "max_average_schema_versions_per_type is too large"
360 );
361 self.config.max_average_schema_versions_per_type = max_versions as u32;
362 self
363 }
364
365 fn finish_config(self) -> Config {
366 let mut config = self.config;
367 if !self.compatible_set {
368 config.share_meta = true;
369 config.compatible = true;
370 config.check_struct_version = false;
371 }
372 config
373 }
374
375 /// Builds a [`Fory`] instance with the current builder configuration.
376 pub fn build(self) -> Fory {
377 let config = self.finish_config();
378 Fory::from_config(config)
379 }
380}
381
382/// The main Fory serialization framework instance.
383///
384/// `Fory` provides high-performance serialization and deserialization with xlang mode,
385/// native mode, reference tracking, and trait object serialization.
386///
387/// # Features
388///
389/// - **Xlang mode**: Default wire format for cross-language payloads
390/// - **Native mode**: Rust-only wire format selected with `.xlang(false)`
391/// - **Schema evolution**: Compatible mode by default, with a same-schema optimization available
392/// - **Reference tracking**: Handles shared and circular references
393/// - **Trait object serialization**: Supports serializing polymorphic trait objects
394/// - **Dynamic depth limiting**: Configurable limit for nested dynamic object serialization
395///
396/// # Examples
397///
398/// Basic usage:
399///
400/// ```rust, ignore
401/// use fory::Fory;
402/// use fory::{ForyEnum, ForyStruct, ForyUnion};
403///
404/// #[derive(ForyStruct)]
405/// struct User {
406/// name: String,
407/// age: u32,
408/// }
409///
410/// let mut fory = Fory::builder().xlang(true).build();
411/// fory.register_by_name::<User>("example.User").unwrap();
412/// let user = User { name: "Alice".to_string(), age: 30 };
413/// let bytes = fory.serialize(&user).unwrap();
414/// let deserialized: User = fory.deserialize(&bytes).unwrap();
415/// ```
416///
417/// Custom configuration:
418///
419/// ```rust
420/// use fory_core::Fory;
421///
422/// let fory = Fory::builder()
423/// .compress_string(true)
424/// .max_dyn_depth(10)
425/// .build();
426/// ```
427pub struct Fory {
428 /// Unique identifier for this Fory instance, used as key in thread-local context maps.
429 id: u64,
430 type_resolver: TypeResolver,
431 /// Lazy-initialized final type resolver (thread-safe, one-time initialization).
432 final_type_resolver: OnceLock<Result<TypeResolver, Error>>,
433 /// Configuration for serialization behavior.
434 ///
435 /// Keep this cold field after the resolver/cache fields. Remote metadata
436 /// limits make Config larger, but serialize hot paths repeatedly access
437 /// the instance id and resolver snapshot, not the cold limit values.
438 config: Config,
439}
440
441impl Default for Fory {
442 fn default() -> Self {
443 Self::builder().build()
444 }
445}
446
447impl Fory {
448 /// Creates a builder for configuring a [`Fory`] instance.
449 pub fn builder() -> ForyBuilder {
450 ForyBuilder::default()
451 }
452
453 fn from_config(config: Config) -> Self {
454 let mut type_resolver = TypeResolver::default();
455 type_resolver.set_compatible(config.compatible);
456 type_resolver.set_xlang(config.xlang);
457 Self {
458 id: FORY_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
459 config,
460 type_resolver,
461 final_type_resolver: OnceLock::new(),
462 }
463 }
464
465 /// Returns whether xlang mode is enabled.
466 pub fn is_xlang(&self) -> bool {
467 self.config.xlang
468 }
469
470 /// Returns whether compatible schema evolution is enabled.
471 ///
472 /// # Returns
473 ///
474 /// `true` if compatible schema evolution is enabled, `false` otherwise.
475 pub fn is_compatible(&self) -> bool {
476 self.config.compatible
477 }
478
479 /// Returns whether string compression is enabled.
480 ///
481 /// # Returns
482 ///
483 /// `true` if meta string compression is enabled, `false` otherwise.
484 pub fn is_compress_string(&self) -> bool {
485 self.config.compress_string
486 }
487
488 /// Returns whether UTF-8 string payload validation is enabled.
489 pub fn is_check_string_read(&self) -> bool {
490 self.config.check_string_read
491 }
492
493 /// Returns whether metadata sharing is enabled.
494 ///
495 /// # Returns
496 ///
497 /// `true` if metadata sharing is enabled, `false` otherwise.
498 pub fn is_share_meta(&self) -> bool {
499 self.config.share_meta
500 }
501
502 /// Returns the maximum depth for nested dynamic object serialization.
503 pub fn get_max_dyn_depth(&self) -> u32 {
504 self.config.max_dyn_depth
505 }
506
507 /// Returns whether class version checking is enabled.
508 ///
509 /// # Returns
510 ///
511 /// `true` if class version checking is enabled, `false` otherwise.
512 pub fn is_check_struct_version(&self) -> bool {
513 self.config.check_struct_version
514 }
515
516 /// Returns a reference to the configuration.
517 pub fn config(&self) -> &Config {
518 &self.config
519 }
520
521 /// Checks whether the final type resolver has already been initialized.
522 ///
523 /// If it has, further type registrations would be silently ignored (the frozen
524 /// snapshot is what serialize/deserialize actually use),so we fail fast with
525 /// a clear error instead.
526 ///
527 /// # errors
528 ///
529 /// returns [`Error::NotAllowed`] when the resolver snapshot has already been
530 /// built (i.e after the first `serialize` / `deserialize` call).
531 fn check_registration_allowed(&self) -> Result<(), Error> {
532 if self.final_type_resolver.get().is_some() {
533 return Err(Error::not_allowed(
534 "Type registration is not allowed after the first serialize/deserialize call. \
535 The type resolver snapshot has already been finalized. \
536 Please complete all type registrations before performing any serialization or deserialization.",
537 ));
538 }
539 Ok(())
540 }
541
542 /// Serializes a value of type `T` into a byte vector.
543 ///
544 /// # Type Parameters
545 ///
546 /// * `T` - The type of the value to serialize. Must implement `Serializer`.
547 ///
548 /// # Arguments
549 ///
550 /// * `record` - A reference to the value to serialize.
551 ///
552 /// # Returns
553 ///
554 /// A `Vec<u8>` containing the serialized data.
555 ///
556 /// # Examples
557 ///
558 /// ```rust, ignore
559 /// use fory::Fory;
560 /// use fory::{ForyEnum, ForyStruct, ForyUnion};
561 ///
562 /// #[derive(ForyStruct)]
563 /// struct Point { x: i32, y: i32 }
564 ///
565 /// let mut fory = Fory::builder().xlang(true).build();
566 /// fory.register_by_name::<Point>("example.Point").unwrap();
567 /// let point = Point { x: 10, y: 20 };
568 /// let bytes = fory.serialize(&point).unwrap();
569 /// ```
570 pub fn serialize<T: Serializer>(&self, record: &T) -> Result<Vec<u8>, Error> {
571 self.with_write_context(
572 |context| match self.serialize_with_context(record, context) {
573 Ok(_) => {
574 let result = context.writer.dump();
575 context.writer.reset();
576 Ok(result)
577 }
578 Err(err) => {
579 context.writer.reset();
580 Err(err)
581 }
582 },
583 )
584 }
585
586 /// Serializes a value of type `T` into the provided byte buffer.
587 ///
588 /// The serialized data is appended to the end of the buffer by default.
589 /// To write from a specific position, resize the buffer before calling this method.
590 ///
591 /// # Type Parameters
592 ///
593 /// * `T` - The type of the value to serialize. Must implement `Serializer`.
594 ///
595 /// # Arguments
596 ///
597 /// * `buf` - A mutable reference to the byte buffer to append the serialized data to.
598 /// The buffer will be resized as needed during serialization.
599 /// * `record` - A reference to the value to serialize.
600 ///
601 /// # Returns
602 ///
603 /// The number of bytes written to the buffer on success, or an error if serialization fails.
604 ///
605 /// # Notes
606 ///
607 /// - Multiple `serialize_to` calls to the same buffer will append data sequentially.
608 ///
609 /// # Examples
610 ///
611 /// Basic usage - appending to a buffer:
612 ///
613 /// ```rust, ignore
614 /// use fory_core::Fory;
615 /// use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
616 ///
617 /// #[derive(ForyStruct)]
618 /// struct Point {
619 /// x: i32,
620 /// y: i32,
621 /// }
622 ///
623 /// let mut fory = Fory::builder().xlang(true).build();
624 /// fory.register_by_name::<Point>("example.Point").unwrap();
625 /// let point = Point { x: 1, y: 2 };
626 ///
627 /// let mut buf = Vec::new();
628 /// let bytes_written = fory.serialize_to(&mut buf, &point).unwrap();
629 /// assert_eq!(bytes_written, buf.len());
630 /// ```
631 ///
632 /// Multiple serializations to the same buffer:
633 ///
634 /// ```rust, ignore
635 /// use fory_core::Fory;
636 /// use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
637 ///
638 /// #[derive(ForyStruct, PartialEq, Debug)]
639 /// struct Point {
640 /// x: i32,
641 /// y: i32,
642 /// }
643 ///
644 /// let mut fory = Fory::builder().xlang(true).build();
645 /// fory.register_by_name::<Point>("example.Point").unwrap();
646 /// let p1 = Point { x: 1, y: 2 };
647 /// let p2 = Point { x: -3, y: 4 };
648 ///
649 /// let mut buf = Vec::new();
650 ///
651 /// // First serialization
652 /// let len1 = fory.serialize_to(&mut buf, &p1).unwrap();
653 /// let offset1 = buf.len();
654 ///
655 /// // Second serialization - appends to existing data
656 /// let len2 = fory.serialize_to(&mut buf, &p2).unwrap();
657 /// let offset2 = buf.len();
658 ///
659 /// assert_eq!(offset1, len1);
660 /// assert_eq!(offset2, len1 + len2);
661 ///
662 /// // Deserialize both objects
663 /// let deserialized1: Point = fory.deserialize(&buf[0..offset1]).unwrap();
664 /// let deserialized2: Point = fory.deserialize(&buf[offset1..offset2]).unwrap();
665 /// assert_eq!(deserialized1, p1);
666 /// assert_eq!(deserialized2, p2);
667 /// ```
668 ///
669 /// Writing to a specific position using `resize`:
670 /// # Notes on `vec.resize()`
671 ///
672 /// When calling `vec.resize(n, 0)`, note that if `n` is smaller than the current length,
673 /// the buffer will be truncated (not shrunk in capacity). The capacity remains unchanged,
674 /// making subsequent writes efficient for buffer reuse patterns:
675 ///
676 /// ```rust, ignore
677 /// use fory_core::Fory;
678 /// use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
679 ///
680 /// #[derive(ForyStruct)]
681 /// struct Point {
682 /// x: i32,
683 /// y: i32,
684 /// }
685 ///
686 /// let mut fory = Fory::builder().xlang(true).build();
687 /// fory.register_by_name::<Point>("example.Point").unwrap();
688 /// let point = Point { x: 1, y: 2 };
689 ///
690 /// let mut buf = Vec::with_capacity(1024);
691 /// buf.resize(16, 0); // Set length to 16 to append the write, capacity stays 1024
692 ///
693 /// let initial_capacity = buf.capacity();
694 /// fory.serialize_to(&mut buf, &point).unwrap();
695 ///
696 /// // Reset to smaller size to append the write - capacity unchanged
697 /// buf.resize(16, 0);
698 /// assert_eq!(buf.capacity(), initial_capacity); // Capacity not shrunk
699 ///
700 /// // Reuse buffer efficiently without reallocation
701 /// fory.serialize_to(&mut buf, &point).unwrap();
702 /// assert_eq!(buf.capacity(), initial_capacity); // Still no reallocation
703 /// ```
704 pub fn serialize_to<T: Serializer>(
705 &self,
706 buf: &mut Vec<u8>,
707 record: &T,
708 ) -> Result<usize, Error> {
709 let start = buf.len();
710 self.with_write_context(|context| {
711 // Context from thread-local would be 'static. but context hold the buffer through `writer` field,
712 // so we should make buffer live longer.
713 // After serializing, `detach_writer` will be called, the writer in context will be set to dangling pointer.
714 // So it's safe to make buf live to the end of this method.
715 let outlive_buffer = unsafe { mem::transmute::<&mut Vec<u8>, &mut Vec<u8>>(buf) };
716 context.attach_writer(Writer::from_buffer(outlive_buffer));
717 let result = self.serialize_with_context(record, context);
718 let written_size = context.writer.len() - start;
719 context.detach_writer();
720 match result {
721 Ok(_) => Ok(written_size),
722 Err(err) => Err(err),
723 }
724 })
725 }
726
727 /// Gets the final type resolver, building it lazily on first access.
728 #[inline(always)]
729 fn get_final_type_resolver(&self) -> Result<&TypeResolver, Error> {
730 let result = self
731 .final_type_resolver
732 .get_or_init(|| self.type_resolver.build_final_type_resolver());
733 result
734 .as_ref()
735 .map_err(|e| Error::type_error(format!("Failed to build type resolver: {}", e)))
736 }
737
738 /// Executes a closure with mutable access to a WriteContext for this Fory instance.
739 /// The context is stored in thread-local storage, eliminating all lock contention.
740 /// Uses fast path caching for O(1) access when using the same Fory instance repeatedly.
741 #[inline(always)]
742 fn with_write_context<R>(
743 &self,
744 f: impl FnOnce(&mut WriteContext) -> Result<R, Error>,
745 ) -> Result<R, Error> {
746 // SAFETY: Thread-local storage is only accessed from the current thread.
747 // We use UnsafeCell to avoid RefCell's runtime borrow checking overhead.
748 // The closure `f` does not recursively call with_write_context, so there's no aliasing.
749 WRITE_CONTEXTS.with(|cache| {
750 let cache = unsafe { &mut *cache.get() };
751 let id = self.id;
752
753 let context = cache.get_or_insert_result(id, || {
754 // Only fetch type resolver when creating a new context
755 let type_resolver = self.get_final_type_resolver()?;
756 Ok(Box::new(WriteContext::new(
757 type_resolver.clone(),
758 self.config.clone(),
759 )))
760 })?;
761 f(context)
762 })
763 }
764
765 /// Serializes a value of type `T` into a byte vector.
766 #[inline(always)]
767 fn serialize_with_context<T: Serializer>(
768 &self,
769 record: &T,
770 context: &mut WriteContext,
771 ) -> Result<(), Error> {
772 let result = self.serialize_with_context_inner::<T>(record, context);
773 context.reset();
774 result
775 }
776
777 #[inline(always)]
778 fn serialize_with_context_inner<T: Serializer>(
779 &self,
780 record: &T,
781 context: &mut WriteContext,
782 ) -> Result<(), Error> {
783 self.write_head::<T>(&mut context.writer);
784 // Use RefMode based on config:
785 // - If track_ref is enabled, use RefMode::Tracking for the root object
786 // - Otherwise, use RefMode::NullOnly which writes NOT_NULL_VALUE_FLAG
787 let ref_mode = if self.config.track_ref {
788 RefMode::Tracking
789 } else {
790 RefMode::NullOnly
791 };
792 // TypeMeta is written inline during serialization (streaming protocol)
793 <T as Serializer>::fory_write(record, context, ref_mode, true, false)?;
794 Ok(())
795 }
796
797 /// Registers a struct type with a numeric type ID for serialization.
798 ///
799 /// # Type Parameters
800 ///
801 /// * `T` - The struct type to register. Must implement `StructSerializer`, `Serializer`, and `ForyDefault`.
802 ///
803 /// # Arguments
804 ///
805 /// * `id` - A unique numeric identifier for the type. This ID is used in the serialized format
806 /// to identify the type during deserialization.
807 ///
808 /// # Panics
809 ///
810 /// May panic if the type ID conflicts with an already registered type.
811 ///
812 /// # Examples
813 ///
814 /// ```rust, ignore
815 /// use fory::Fory;
816 /// use fory::{ForyEnum, ForyStruct, ForyUnion};
817 ///
818 /// #[derive(ForyStruct)]
819 /// struct User { name: String, age: u32 }
820 ///
821 /// let mut fory = Fory::builder().xlang(true).build();
822 /// fory.register::<User>(100).unwrap();
823 /// ```
824 pub fn register<T: 'static + StructSerializer + Serializer + ForyDefault>(
825 &mut self,
826 id: u32,
827 ) -> Result<(), Error> {
828 self.check_registration_allowed()?;
829 self.type_resolver.register::<T>(id)
830 }
831
832 /// Register a union type with a numeric type ID.
833 ///
834 /// This is intended for union-compatible enums generated by the compiler.
835 pub fn register_union<T: 'static + StructSerializer + Serializer + ForyDefault>(
836 &mut self,
837 id: u32,
838 ) -> Result<(), Error> {
839 self.check_registration_allowed()?;
840 self.type_resolver.register_union::<T>(id)
841 }
842
843 /// Registers a struct type with a qualified type name for xlang serialization.
844 ///
845 /// # Type Parameters
846 ///
847 /// * `T` - The struct type to register. Must implement `StructSerializer`, `Serializer`, and `ForyDefault`.
848 ///
849 /// # Arguments
850 ///
851 /// * `name` - The type name, optionally prefixed with a namespace separated by `.`.
852 /// For example, `"com.example.User"` uses namespace `"com.example"` and type name `"User"`.
853 /// Use `"User"` for the default namespace.
854 ///
855 /// # Notes
856 ///
857 /// This registration method is preferred for xlang serialization because it uses
858 /// human-readable type identifiers instead of numeric IDs, which improves compatibility
859 /// across different language implementations.
860 ///
861 /// # Examples
862 ///
863 /// The example uses xlang mode because name-based registration is the preferred
864 /// registration style for cross-language payloads.
865 ///
866 /// ```rust, ignore
867 /// use fory::Fory;
868 /// use fory::{ForyEnum, ForyStruct, ForyUnion};
869 ///
870 /// #[derive(ForyStruct)]
871 /// struct User { name: String, age: u32 }
872 ///
873 /// let mut fory = Fory::builder().xlang(true).build();
874 /// fory.register_by_name::<User>("com.example.User").unwrap();
875 /// ```
876 pub fn register_by_name<T: 'static + StructSerializer + Serializer + ForyDefault>(
877 &mut self,
878 name: &str,
879 ) -> Result<(), Error> {
880 self.check_registration_allowed()?;
881 self.type_resolver.register_by_name::<T>(name)
882 }
883
884 /// Register a union type with a qualified type name.
885 ///
886 /// This is intended for union-compatible enums generated by the compiler.
887 pub fn register_union_by_name<T: 'static + StructSerializer + Serializer + ForyDefault>(
888 &mut self,
889 name: &str,
890 ) -> Result<(), Error> {
891 self.check_registration_allowed()?;
892 self.type_resolver.register_union_by_name::<T>(name)
893 }
894
895 /// Registers a custom serializer type with a numeric type ID.
896 ///
897 /// # Type Parameters
898 ///
899 /// * `T` - The type to register. Must implement `Serializer` and `ForyDefault`.
900 /// Unlike `register()`, this does not require `StructSerializer`, making it suitable
901 /// for non-struct types or types with custom serialization logic.
902 ///
903 /// # Arguments
904 ///
905 /// * `id` - A unique numeric identifier for the type.
906 ///
907 /// # Use Cases
908 ///
909 /// Use this method to register:
910 /// - Enum types with custom serialization
911 /// - Wrapper types
912 /// - Types with hand-written `Serializer` implementations
913 ///
914 /// # Examples
915 ///
916 /// ```rust, ignore
917 /// use fory_core::Fory;
918 ///
919 /// let mut fory = Fory::builder().xlang(false).build();
920 /// fory.register_serializer::<MyCustomType>(200).unwrap();
921 /// ```
922 pub fn register_serializer<T: Serializer + ForyDefault>(
923 &mut self,
924 id: u32,
925 ) -> Result<(), Error> {
926 self.check_registration_allowed()?;
927 self.type_resolver.register_serializer::<T>(id)
928 }
929
930 /// Registers a custom serializer type with a qualified type name.
931 ///
932 /// # Type Parameters
933 ///
934 /// * `T` - The type to register. Must implement `Serializer` and `ForyDefault`.
935 ///
936 /// # Arguments
937 ///
938 /// * `name` - The type name, optionally prefixed with a namespace separated by `.`.
939 ///
940 /// # Notes
941 ///
942 /// This is the named equivalent of `register_serializer()`, preferred for
943 /// xlang serialization scenarios.
944 ///
945 pub fn register_serializer_by_name<T: Serializer + ForyDefault>(
946 &mut self,
947 name: &str,
948 ) -> Result<(), Error> {
949 self.check_registration_allowed()?;
950 self.type_resolver.register_serializer_by_name::<T>(name)
951 }
952
953 /// Writes the serialization header to the writer.
954 #[inline(always)]
955 pub fn write_head<T: Serializer>(&self, writer: &mut Writer) {
956 const HEAD_SIZE: usize = 10;
957 writer.reserve(T::fory_reserved_space() + SIZE_OF_REF_AND_TYPE + HEAD_SIZE);
958 let bitmap = if self.config.xlang {
959 IS_CROSS_LANGUAGE_FLAG
960 } else {
961 0
962 };
963 writer.write_u8(bitmap);
964 }
965
966 /// Deserializes data from a byte slice into a value of type `T`.
967 ///
968 /// # Type Parameters
969 ///
970 /// * `T` - The target type to deserialize into. Must implement `Serializer` and `ForyDefault`.
971 ///
972 /// # Arguments
973 ///
974 /// * `bf` - The byte slice containing the serialized data.
975 ///
976 /// # Returns
977 ///
978 /// * `Ok(T)` - The deserialized value on success.
979 /// * `Err(Error)` - An error if deserialization fails (e.g., invalid format, type mismatch).
980 ///
981 /// # Panics
982 ///
983 /// Panics in debug mode if there are unread bytes remaining after successful deserialization,
984 /// indicating a potential protocol violation.
985 ///
986 /// # Examples
987 ///
988 /// ```rust, ignore
989 /// use fory::Fory;
990 /// use fory::{ForyEnum, ForyStruct, ForyUnion};
991 ///
992 /// #[derive(ForyStruct)]
993 /// struct Point { x: i32, y: i32 }
994 ///
995 /// let mut fory = Fory::builder().xlang(true).build();
996 /// fory.register_by_name::<Point>("example.Point").unwrap();
997 /// let point = Point { x: 10, y: 20 };
998 /// let bytes = fory.serialize(&point).unwrap();
999 /// let deserialized: Point = fory.deserialize(&bytes).unwrap();
1000 /// ```
1001 pub fn deserialize<T: Serializer + ForyDefault>(&self, bf: &[u8]) -> Result<T, Error> {
1002 self.with_read_context(|context| {
1003 let outlive_buffer = unsafe { mem::transmute::<&[u8], &[u8]>(bf) };
1004 context.attach_reader(Reader::new(outlive_buffer));
1005 context.remaining_graph_memory_bytes = self.config.max_graph_memory_bytes;
1006 let result = self.deserialize_with_context(context);
1007 context.detach_reader();
1008 result
1009 })
1010 }
1011
1012 /// Deserializes data from a `Reader` into a value of type `T`.
1013 ///
1014 /// This method is the paired read operation for [`serialize_to`](Self::serialize_to).
1015 /// It reads serialized data from the current position of the reader and automatically
1016 /// advances the cursor to the end of the read data, making it suitable for reading
1017 /// multiple objects sequentially from the same buffer.
1018 ///
1019 /// # Type Parameters
1020 ///
1021 /// * `T` - The target type to deserialize into. Must implement `Serializer` and `ForyDefault`.
1022 ///
1023 /// # Arguments
1024 ///
1025 /// * `reader` - A mutable reference to the `Reader` containing the serialized data.
1026 /// The reader's cursor will be advanced to the end of the deserialized data.
1027 ///
1028 /// # Returns
1029 ///
1030 /// * `Ok(T)` - The deserialized value on success.
1031 /// * `Err(Error)` - An error if deserialization fails (e.g., invalid format, type mismatch).
1032 ///
1033 /// # Notes
1034 ///
1035 /// - The reader's cursor is automatically updated after each successful read.
1036 /// - This method is ideal for reading multiple objects from the same buffer sequentially.
1037 /// - See [`serialize_to`](Self::serialize_to) for complete usage examples.
1038 ///
1039 /// # Examples
1040 ///
1041 /// Basic usage:
1042 ///
1043 /// ```rust, ignore
1044 /// use fory_core::{Fory, Reader};
1045 /// use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
1046 ///
1047 /// #[derive(ForyStruct)]
1048 /// struct Point { x: i32, y: i32 }
1049 ///
1050 /// let mut fory = Fory::builder().xlang(true).build();
1051 /// fory.register_by_name::<Point>("example.Point").unwrap();
1052 /// let point = Point { x: 10, y: 20 };
1053 ///
1054 /// let mut buf = Vec::new();
1055 /// fory.serialize_to(&mut buf, &point).unwrap();
1056 ///
1057 /// let mut reader = Reader::new(&buf);
1058 /// let deserialized: Point = fory.deserialize_from(&mut reader).unwrap();
1059 /// ```
1060 pub fn deserialize_from<T: Serializer + ForyDefault>(
1061 &self,
1062 reader: &mut Reader,
1063 ) -> Result<T, Error> {
1064 self.with_read_context(|context| {
1065 let outlive_buffer = unsafe { mem::transmute::<&[u8], &[u8]>(reader.bf) };
1066 let mut new_reader = Reader::new(outlive_buffer);
1067 new_reader.set_cursor(reader.cursor);
1068 context.attach_reader(new_reader);
1069 context.remaining_graph_memory_bytes = self.config.max_graph_memory_bytes;
1070 let result = self.deserialize_with_context(context);
1071 let end = context.detach_reader().get_cursor();
1072 reader.set_cursor(end);
1073 result
1074 })
1075 }
1076
1077 /// Executes a closure with mutable access to a ReadContext for this Fory instance.
1078 /// The context is stored in thread-local storage, eliminating all lock contention.
1079 /// Uses fast path caching for O(1) access when using the same Fory instance repeatedly.
1080 #[inline(always)]
1081 fn with_read_context<R>(
1082 &self,
1083 f: impl FnOnce(&mut ReadContext) -> Result<R, Error>,
1084 ) -> Result<R, Error> {
1085 // SAFETY: Thread-local storage is only accessed from the current thread.
1086 // We use UnsafeCell to avoid RefCell's runtime borrow checking overhead.
1087 // The closure `f` does not recursively call with_read_context, so there's no aliasing.
1088 READ_CONTEXTS.with(|cache| {
1089 let cache = unsafe { &mut *cache.get() };
1090 let id = self.id;
1091
1092 let context = cache.get_or_insert_result(id, || {
1093 // Only fetch type resolver when creating a new context
1094 let type_resolver = self.get_final_type_resolver()?;
1095 Ok(Box::new(ReadContext::new(
1096 type_resolver.clone(),
1097 self.config.clone(),
1098 )))
1099 })?;
1100 f(context)
1101 })
1102 }
1103
1104 #[inline(always)]
1105 fn deserialize_with_context<T: Serializer + ForyDefault>(
1106 &self,
1107 context: &mut ReadContext,
1108 ) -> Result<T, Error> {
1109 let result = self.deserialize_with_context_inner::<T>(context);
1110 context.reset();
1111 result
1112 }
1113
1114 #[inline(always)]
1115 fn deserialize_with_context_inner<T: Serializer + ForyDefault>(
1116 &self,
1117 context: &mut ReadContext,
1118 ) -> Result<T, Error> {
1119 self.read_head(&mut context.reader)?;
1120 // Use RefMode based on config:
1121 // - If track_ref is enabled, use RefMode::Tracking for the root object
1122 // - Otherwise, use RefMode::NullOnly
1123 let ref_mode = if self.config.track_ref {
1124 RefMode::Tracking
1125 } else {
1126 RefMode::NullOnly
1127 };
1128 let result = <T as Serializer>::fory_read(context, ref_mode, true);
1129 context.ref_reader.resolve_callbacks();
1130 result
1131 }
1132
1133 #[inline(always)]
1134 fn read_head(&self, reader: &mut Reader) -> Result<(), Error> {
1135 let bitmap = reader.read_u8()?;
1136 let expected = if self.config.xlang {
1137 IS_CROSS_LANGUAGE_FLAG
1138 } else {
1139 0
1140 };
1141 if bitmap != expected {
1142 return self.read_head_slow(bitmap, expected);
1143 }
1144 Ok(())
1145 }
1146
1147 #[cold]
1148 #[inline(never)]
1149 fn read_head_slow(&self, bitmap: u8, expected: u8) -> Result<(), Error> {
1150 const KNOWN_FLAGS: u8 = IS_CROSS_LANGUAGE_FLAG | IS_OUT_OF_BAND_FLAG;
1151 ensure!(
1152 (bitmap & !KNOWN_FLAGS) == 0 && (bitmap & IS_OUT_OF_BAND_FLAG) == 0,
1153 Error::invalid_data("unsupported root header bitmap")
1154 );
1155 ensure!(
1156 (bitmap & IS_CROSS_LANGUAGE_FLAG) == (expected & IS_CROSS_LANGUAGE_FLAG),
1157 Error::invalid_data("header bitmap mismatch at xlang bit")
1158 );
1159 Ok(())
1160 }
1161}
1162
1163#[cfg(test)]
1164mod tests {
1165 use super::Fory;
1166
1167 #[test]
1168 fn compatible_defaults_and_overrides() {
1169 let default_xlang = Fory::builder().xlang(true).finish_config();
1170 let default_native = Fory::builder().xlang(false).finish_config();
1171 let explicit_same_schema = Fory::builder()
1172 .compatible(false)
1173 .xlang(true)
1174 .finish_config();
1175 let explicit_same_schema_reverse_order = Fory::builder()
1176 .xlang(true)
1177 .compatible(false)
1178 .finish_config();
1179
1180 assert!(default_xlang.compatible);
1181 assert!(default_xlang.share_meta);
1182 assert!(!default_xlang.check_struct_version);
1183 assert!(default_native.compatible);
1184 assert!(default_native.share_meta);
1185 assert!(!default_native.check_struct_version);
1186
1187 assert!(!explicit_same_schema.compatible);
1188 assert!(!explicit_same_schema.share_meta);
1189 assert!(explicit_same_schema.check_struct_version);
1190 assert!(!explicit_same_schema_reverse_order.compatible);
1191 assert!(!explicit_same_schema_reverse_order.share_meta);
1192 assert!(explicit_same_schema_reverse_order.check_struct_version);
1193 }
1194}