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