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