This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new fc514e18 docs: add document for customized map collection serializers 
(#2351)
fc514e18 is described below

commit fc514e1851ed428ea7f6bdb8152924ddc2e9e67c
Author: Shawn Yang <[email protected]>
AuthorDate: Wed Jun 18 20:20:02 2025 +0800

    docs: add document for customized map collection serializers (#2351)
    
    ## What does this PR do?
    
    This PR add documents for customized map collection serializers
    
    ## Related issues
    
    <!--
    Is there any related issue? Please attach here.
    
    - #xxxx0
    - #xxxx1
    - #xxxx2
    -->
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    -->
---
 docs/guide/DEVELOPMENT.md              |  15 +
 docs/guide/java_serialization_guide.md | 817 ++++++++++++++++++++++++++++++++-
 2 files changed, 819 insertions(+), 13 deletions(-)

diff --git a/docs/guide/DEVELOPMENT.md b/docs/guide/DEVELOPMENT.md
index dffe0995..0379d9f6 100644
--- a/docs/guide/DEVELOPMENT.md
+++ b/docs/guide/DEVELOPMENT.md
@@ -120,3 +120,18 @@ npm run test
 
 - node 14+
 - npm 8+
+
+### Lint Markdown Docs
+
+```bash
+# Install prettier globally
+npm install -g prettier
+
+# Fix markdown files
+prettier --write "**/*.md"
+```
+
+#### Environment Requirements
+
+- node 14+
+- npm 8+
diff --git a/docs/guide/java_serialization_guide.md 
b/docs/guide/java_serialization_guide.md
index af85d1bb..728b5f1c 100644
--- a/docs/guide/java_serialization_guide.md
+++ b/docs/guide/java_serialization_guide.md
@@ -108,10 +108,10 @@ public class Example {
 }
 ```
 
-## ForyBuilder  options
+## ForyBuilder options
 
 | Option Name                         | Description                            
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
-|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
+| ----------------------------------- | 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
 | `timeRefIgnored`                    | Whether to ignore reference tracking 
of all time types registered in `TimeSerializers` and subclasses of those types 
when ref tracking is enabled. If ignored, ref tracking of every time type can 
be enabled by invoking `Fory#registerSerializer(Class, Serializer)`. For 
example, `fory.registerSerializer(Date.class, new DateSerializer(fory, true))`. 
Note that enabling ref tracking should happen before serializer codegen of any 
types which contain time  [...]
 | `compressInt`                       | Enables or disables int compression 
for smaller size.                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                 [...]
 | `compressLong`                      | Enables or disables long compression 
for smaller size.                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                [...]
@@ -211,8 +211,7 @@ sophisticated compression techniques to minimize overhead, 
there is still some a
 class metadata.
 
 To further reduce metadata costs, Fory introduces a class metadata sharing 
mechanism, which allows the metadata to be
-sent to the deserialization process only once. For more details, please refer 
to the [Meta Sharing](#MetaSharing)
-section.
+sent to the deserialization process only once. For more details, please refer 
to the [Meta 
Sharing](https://fory.apache.org/docs/specification/fory_java_serialization_spec#meta-share)
 specification.
 
 ### Smaller size
 
@@ -234,9 +233,9 @@ For long compression, fory support two encoding:
   - Otherwise write as 9 bytes: `| 0b1 | little-endian 8bytes long |`
 - Fory PVL(Progressive Variable-length Long) Encoding:
   - First bit in every byte indicate whether has next byte. if first bit is 
set, then next byte will be read util
-      first bit of next byte is unset.
+    first bit of next byte is unset.
   - Negative number will be converted to positive number by `(v << 1) ^ (v >> 
63)` to reduce cost of small negative
-      numbers.
+    numbers.
 
 If a number are `long` type, it can't be represented by smaller bytes mostly, 
the compression won't get good enough
 result,
@@ -266,10 +265,8 @@ SomeClass copied = fory.copy(a);
 ### Implement a customized serializer
 
 In some cases, you may want to implement a serializer for your type, 
especially some class customize serialization by
-JDK
-writeObject/writeReplace/readObject/readResolve, which is very inefficient. 
For example, you don't want
-following `Foo#writeObject`
-got invoked, you can take following `FooSerializer` as an example:
+JDK `writeObject/writeReplace/readObject/readResolve`, which is very 
inefficient. For example, if you don't want
+following `Foo#writeObject` got invoked, you can take following 
`FooSerializer` as an example:
 
 ```java
 class Foo {
@@ -307,6 +304,799 @@ Fory fory = getFory();
 fory.registerSerializer(Foo.class, new FooSerializer(fory));
 ```
 
+### Implement Collection Serializer
+
+Similar to maps, when implementing a serializer for a custom Collection type, 
you must extend `CollectionSerializer` or `AbstractCollectionSerializer`.
+The key difference between these two is that `AbstractCollectionSerializer` 
can serialize a class which has a collection-like structure but is not a java 
Collection subtype.
+
+For collection serializer, this is a special parameter `supportCodegenHook` 
needs be configured:
+
+- When `true`:
+
+  - Enables optimized access to collection elements and JIT compilation for 
better performance
+  - Direct serialization invocation and inline for map key-value items without 
dynamic serializer dispatch cost.
+  - Better performance for standard collection types
+  - Recommended for most collections
+
+- When `false`:
+  - Uses interfaced-based element access and dynamic serializer dispatch for 
elements, which have higer cost
+  - More flexible for custom collection types
+  - Required when collection has special serialization needs
+  - Handles complex collection implementations
+
+#### Implement Collection Serializer with JIT support
+
+When implementing a Collection serializer with JIT support, you can leverage 
Fory's existing binary format and collection serialization infrastructure. The 
key is to properly implement the `onCollectionWrite` and `newCollection` 
methods to handle metadata while letting Fory handle the element serialization.
+
+Here's an example:
+
+```java
+public class CustomCollectionSerializer<T extends Collection> extends 
CollectionSerializer<T> {
+    public CustomCollectionSerializer(Fory fory, Class<T> cls) {
+        // supportCodegenHook controls whether to use JIT compilation
+        super(fory, cls, true);
+    }
+
+    @Override
+    public Collection onCollectionWrite(MemoryBuffer buffer, T value) {
+        // Write collection size
+        buffer.writeVarUint32Small7(value.size());
+        // Write any additional collection metadata
+        return value;
+    }
+
+    @Override
+    public Collection newCollection(MemoryBuffer buffer) {
+        // Create new collection instance
+        Collection collection = super.newCollection(buffer);
+        // Read and set collection size
+        int numElements = getAndClearNumElements();
+        setNumElements(numElements);
+        return collection;
+    }
+}
+```
+
+Note that please invoke `setNumElements` when implementing `newCollection` to 
let fory know how many elements to deserialize.
+
+#### Implement a totally-customzied Collection Serializer without JIT
+
+Sometimes you need to serialize a collection type that uses primitive arrays 
or has special requirements.
+In such cases, you can implement a serializer with JIT disabled and directly 
override the `write` and `read` methods.
+
+This approach:
+
+- Gives you full control over serialization
+- Works well with primitive arrays
+- Bypasses collection iteration overhead
+- Allows direct memory access
+
+Here's an example of a custom integer list backed by a primitive array:
+
+```java
+class IntList extends AbstractCollection<Integer> {
+    private final int[] elements;
+    private final int size;
+
+    public IntList(int size) {
+        this.elements = new int[size];
+        this.size = size;
+    }
+
+    public IntList(int[] elements, int size) {
+        this.elements = elements;
+        this.size = size;
+    }
+
+    @Override
+    public Iterator<Integer> iterator() {
+        return new Iterator<Integer>() {
+            private int index = 0;
+
+            @Override
+            public boolean hasNext() {
+                return index < size;
+            }
+
+            @Override
+            public Integer next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                return elements[index++];
+            }
+        };
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+
+    public int get(int index) {
+        if (index >= size) {
+            throw new IndexOutOfBoundsException();
+        }
+        return elements[index];
+    }
+
+    public void set(int index, int value) {
+        if (index >= size) {
+            throw new IndexOutOfBoundsException();
+        }
+        elements[index] = value;
+    }
+
+    public int[] getElements() {
+        return elements;
+    }
+}
+
+class IntListSerializer extends AbstractCollectionSerializer<IntList> {
+    public IntListSerializer(Fory fory) {
+        // Disable JIT since we're handling serialization directly
+        super(fory, IntList.class, false);
+    }
+
+    @Override
+    public void write(MemoryBuffer buffer, IntList value) {
+        // Write size
+        buffer.writeVarUint32Small7(value.size());
+
+        // Write elements directly as primitive ints
+        int[] elements = value.getElements();
+        for (int i = 0; i < value.size(); i++) {
+            buffer.writeVarInt32(elements[i]);
+        }
+    }
+
+    @Override
+    public IntList read(MemoryBuffer buffer) {
+        // Read size
+        int size = buffer.readVarUint32Small7();
+
+        // Create array and read elements
+        int[] elements = new int[size];
+        for (int i = 0; i < size; i++) {
+            elements[i] = buffer.readVarInt32();
+        }
+
+        return new IntList(elements, size);
+    }
+
+    // These methods are not used when JIT is disabled
+    @Override
+    public Collection onCollectionWrite(MemoryBuffer buffer, IntList value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Collection newCollection(MemoryBuffer buffer) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IntList onCollectionRead(Collection collection) {
+        throw new UnsupportedOperationException();
+    }
+}
+```
+
+Key Points:
+
+1. **Primitive Array Storage**:
+
+   - Uses `int[]` for direct storage
+   - Avoids boxing/unboxing overhead
+   - Provides efficient memory layout
+   - Enables direct array access
+
+2. **Direct Serialization**:
+
+   - Write size first
+   - Write primitive values directly
+   - No iteration overhead
+   - No boxing/unboxing during serialization
+
+3. **Direct Deserialization**:
+
+   - Read size first
+   - Create primitive array
+   - Read values directly into array
+   - Create list with populated array
+
+4. **Disabled JIT**:
+   - Set `supportCodegenHook=false`
+   - Override `write`/`read` methods
+   - Skip collection view pattern
+   - Full control over serialization format
+
+When to Use: this approach is best when:
+
+- Working with primitive types
+- Need maximum performance
+- Want to minimize memory overhead
+- Have special serialization requirements
+
+Usage Example:
+
+```java
+// Create and populate list
+IntList list = new IntList(3);
+list.set(0, 1);
+list.set(1, 2);
+list.set(2, 3);
+
+// Serialize
+byte[] bytes = fory.serialize(list);
+
+// Deserialize
+IntList newList = (IntList) fory.deserialize(bytes);
+```
+
+This implementation is particularly efficient for scenarios where:
+
+- You're working exclusively with integers
+- Performance is critical
+- Memory efficiency is important
+- Serialization overhead needs to be minimized
+
+Remember that while this approach gives up some of Fory's optimizations, it 
can provide better performance for specific use cases involving primitive types 
and direct array access.
+
+#### Implement Serializer for Collection-like Types
+
+Sometimes you may want to implement a serializer for a type that behaves like 
a collection but isn't a standard Java Collection. This section demonstrates 
how to implement a serializer for such types.
+
+The key principles for collection-like type serialization are:
+
+1. Extend `AbstractCollectionSerializer` for custom collection-like types
+2. Enable JIT optimization with `supportCodegenHook`
+3. Provide efficient element access through views
+4. Maintain proper size tracking
+
+Here's an example:
+
+```java
+class CustomCollectionLike {
+    private final Object[] elements;
+    private final int size;
+
+    public CustomCollectionLike(int size) {
+        this.elements = new Object[size];
+        this.size = size;
+    }
+
+    // Constructor for wrapping existing array
+    public CustomCollectionLike(Object[] elements, int size) {
+        this.elements = elements;
+        this.size = size;
+    }
+
+    public Object get(int index) {
+        if (index >= size) {
+            throw new IndexOutOfBoundsException();
+        }
+        return elements[index];
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public Object[] getElements() {
+        return elements;
+    }
+}
+
+// A view class that extends AbstractCollection for simpler implementation
+class CollectionView extends AbstractCollection<Object> {
+    private final Object[] elements;
+    private final int size;
+    private int writeIndex;
+
+    // Constructor for serialization (wrapping existing array)
+    public CollectionView(CustomCollectionLike collection) {
+        this.elements = collection.getElements();
+        this.size = collection.size();
+    }
+
+    // Constructor for deserialization
+    public CollectionView(int size) {
+        this.size = size;
+        this.elements = new Object[size];
+    }
+
+    @Override
+    public Iterator<Object> iterator() {
+        return new Iterator<Object>() {
+            private int index = 0;
+
+            @Override
+            public boolean hasNext() {
+                return index < size;
+            }
+
+            @Override
+            public Object next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                return elements[index++];
+            }
+        };
+    }
+
+    @Override
+    public boolean add(Object element) {
+        if (writeIndex >= size) {
+            throw new IllegalStateException("Collection is full");
+        }
+        elements[writeIndex++] = element;
+        return true;
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+
+    public Object[] getElements() {
+        return elements;
+    }
+}
+
+class CustomCollectionSerializer extends 
AbstractCollectionSerializer<CustomCollectionLike> {
+    public CustomCollectionSerializer(Fory fory) {
+        super(fory, CustomCollectionLike.class, true);
+    }
+
+    @Override
+    public Collection onCollectionWrite(MemoryBuffer buffer, 
CustomCollectionLike value) {
+        buffer.writeVarUint32Small7(value.size());
+        return new CollectionView(value);
+    }
+
+    @Override
+    public Collection newCollection(MemoryBuffer buffer) {
+        int numElements = buffer.readVarUint32Small7();
+        setNumElements(numElements);
+        return new CollectionView(numElements);
+    }
+
+    @Override
+    public CustomCollectionLike onCollectionRead(Collection collection) {
+        CollectionView view = (CollectionView) collection;
+        return new CustomCollectionLike(view.getElements(), view.size());
+    }
+}
+```
+
+Key takeways:
+
+1. **Collection Structure**:
+
+   - Array-based storage for elements
+   - Fixed size after creation
+   - Direct element access
+   - Size tracking
+
+2. **View Implementation**:
+
+   - Extends `AbstractCollection` for simplicity
+   - Provides iterator for element access
+   - Implements `add()` for deserialization
+   - Shares array reference with original type
+
+3. **Serializer Features**:
+
+   - Uses `supportCodegenHook=true` for JIT optimization
+   - Shares array references when possible
+   - Maintains proper size tracking
+   - Uses view pattern for serialization
+
+4. **Performance Aspects**:
+   - Direct array access
+   - Minimal object creation
+   - Array sharing between instances
+   - Efficient iteration
+
+Note that this implementation provides better performance at the cost of 
flexibility. Consider your specific use case when choosing this approach.
+
+### Implement Map Serializer
+
+When implementing a serializer for a custom Map type, you must extend 
`MapSerializer` or `AbstractMapSerializer`. The key difference between these 
two is that `AbstractMapSerializer` can serialize a class which has a map-like 
structure but is not a java Map subtype.
+
+Similiar to collection serializer, this is a special parameter 
`supportCodegenHook` needs be configured:
+
+- When `true`:
+
+  - Enables optimized access to map elements and JIT compilation for better 
performance
+  - Direct serialization invocation and inline for map key-value items without 
dynamic serializer dispatch cost.
+  - Better performance for standard map types
+  - Recommended for most maps
+
+- When `false`:
+  - Uses interfaced-based element access and dynamic serializer dispatch for 
elements, which have higer cost
+  - More flexible for custom map types
+  - Required when map has special serialization needs
+  - Handles complex map implementations
+
+#### Implement Map Serializer with JIT support
+
+When implementing a Map serializer with JIT support, you can leverage Fory's 
existing chunk-based binary format and map serialization infrastructure. The 
key is to properly implement the `onMapWrite` and `newMap` methods to handle 
metadata while letting Fory handle the map key-value serialization.
+
+Here's an example of implementing a custom map serializer:
+
+```java
+public class CustomMapSerializer<T extends Map> extends MapSerializer<T> {
+    public CustomMapSerializer(Fory fory, Class<T> cls) {
+        // supportCodegenHook is a critical parameter that determines 
serialization behavior
+        super(fory, cls, true);
+    }
+
+    @Override
+    public Map onMapWrite(MemoryBuffer buffer, T value) {
+        // Write map size
+        buffer.writeVarUint32Small7(value.size());
+        // Write any additional map metadata here
+        return value;
+    }
+
+    @Override
+    public Map newMap(MemoryBuffer buffer) {
+        // Read map size
+        int numElements = buffer.readVarUint32Small7();
+        setNumElements(numElements);
+        // Create and return new map instance
+        T map = (T) new HashMap(numElements);
+        fory.getRefResolver().reference(map);
+        return map;
+    }
+}
+```
+
+Note that please invoke `setNumElements` when implementing `newMap` to let 
fory know how many elements to deserialize.
+
+#### Implement a totally-customzied Map Serializer without JIT
+
+Sometimes you may need complete control over the serialization process, or 
your map type might have special requirements that don't fit the standard 
patterns. In such cases, you can implement a serializer with 
`supportCodegenHook=false` and directly override the `write` and `read` methods.
+
+This approach:
+
+- Gives you full control over serialization
+- Allows custom binary format
+- Bypasses the standard map serialization pattern
+- May be simpler for special cases
+
+Here's an example:
+
+```java
+class FixedValueMap extends AbstractMap<String, Integer> {
+    private final Set<String> keys;
+    private final int fixedValue;
+
+    public FixedValueMap(Set<String> keys, int fixedValue) {
+        this.keys = keys;
+        this.fixedValue = fixedValue;
+    }
+
+    @Override
+    public Set<Entry<String, Integer>> entrySet() {
+        Set<Entry<String, Integer>> entries = new HashSet<>();
+        for (String key : keys) {
+            entries.add(new SimpleEntry<>(key, fixedValue));
+        }
+        return entries;
+    }
+
+    @Override
+    public Integer get(Object key) {
+        return keys.contains(key) ? fixedValue : null;
+    }
+
+    public Set<String> getKeys() {
+        return keys;
+    }
+
+    public int getFixedValue() {
+        return fixedValue;
+    }
+}
+
+class FixedValueMapSerializer extends AbstractMapSerializer<FixedValueMap> {
+    public FixedValueMapSerializer(Fory fory) {
+        // Disable codegen since we're handling serialization directly
+        super(fory, FixedValueMap.class, false);
+    }
+
+    @Override
+    public void write(MemoryBuffer buffer, FixedValueMap value) {
+        // Write the fixed value
+        buffer.writeInt32(value.getFixedValue());
+        // Write the number of keys
+        buffer.writeVarUint32Small7(value.getKeys().size());
+        // Write each key
+        for (String key : value.getKeys()) {
+            buffer.writeString(key);
+        }
+    }
+
+    @Override
+    public FixedValueMap read(MemoryBuffer buffer) {
+        // Read the fixed value
+        int fixedValue = buffer.readInt32();
+        // Read the number of keys
+        int size = buffer.readVarUint32Small7();
+        Set<String> keys = new HashSet<>(size);
+        for (int i = 0; i < size; i++) {
+            keys.add(buffer.readString());
+        }
+        return new FixedValueMap(keys, fixedValue);
+    }
+
+    // These methods are not used when supportCodegenHook is false
+    @Override
+    public Map onMapWrite(MemoryBuffer buffer, FixedValueMap value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public FixedValueMap onMapRead(Map map) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public FixedValueMap onMapCopy(Map map) {
+        throw new UnsupportedOperationException();
+    }
+}
+```
+
+Key Points:
+
+1. **Disable Codegen**:
+
+   - Set `supportCodegenHook=false` in constructor
+   - Fory will use your `write`/`read` methods directly
+   - No JIT optimization will be applied
+   - Full control over serialization format
+
+2. **Write Method**:
+
+   - Handle all serialization manually
+   - Write custom fields first
+   - Write map entries in your preferred format
+   - Control the exact binary layout
+
+3. **Read Method**:
+
+   - Handle all deserialization manually
+   - Read in same order as written
+   - Create and populate map instance
+   - Restore custom fields
+
+4. **Unused Methods**:
+   - `onMapWrite`, `onMapRead`, `onMapCopy` are not used
+   - Can throw `UnsupportedOperationException`
+   - Only `write` and `read` are important
+
+When to Use: this approach is best when
+
+- Map has custom fields or metadata
+- Special serialization format is needed
+- Complete control over binary format is required
+- Standard map patterns don't fit
+
+Trade-offs
+
+1. **Advantages**:
+
+   - Complete control over serialization
+   - Custom binary format possible
+   - Simpler implementation for special cases
+   - Direct handling of custom fields
+
+2. **Disadvantages**:
+   - No JIT optimization
+   - Potentially lower performance
+   - Manual handling of all serialization
+   - More code to maintain
+
+Remember that disabling codegen means giving up some performance optimizations 
that Fory provides. Only use this approach when the standard map serialization 
pattern doesn't meet your needs.
+
+#### Implement Serializer for Map-like Types
+
+Sometimes you may want to implement a serializer for a type that behaves like 
a map but isn't a standard Java map. This section demonstrates how to implement 
a serializer for such types.
+
+The key principles for map-like type serialization are:
+
+1. Extend `AbstractMapSerializer` for custom collection-like types
+2. Enable JIT optimization with `supportCodegenHook`
+3. Provide efficient element access through views
+4. Maintain proper size tracking
+
+Here's a complete example:
+
+```java
+// It's better to make it to implements the java.util.Map interface, in this 
way we don't have to implement such serializers by ourself.
+class CustomMapLike {
+    private final Object[] keyArray;
+    private final Object[] valueArray;
+    private final int size;
+
+    // Constructor for creating new instance
+    public CustomMapLike(int initialCapacity) {
+        this.keyArray = new Object[initialCapacity];
+        this.valueArray = new Object[initialCapacity];
+        this.size = 0;
+    }
+
+    // Constructor for wrapping existing arrays
+    public CustomMapLike(Object[] keyArray, Object[] valueArray, int size) {
+        this.keyArray = keyArray;
+        this.valueArray = valueArray;
+        this.size = size;
+    }
+
+    public Integer get(String key) {
+        for (int i = 0; i < size; i++) {
+            if (key.equals(keyArray[i])) {
+                return (Integer) valueArray[i];
+            }
+        }
+        return null;
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public Object[] getKeyArray() {
+        return keyArray;
+    }
+
+    public Object[] getValueArray() {
+        return valueArray;
+    }
+}
+
+class MapView extends AbstractMap<Object, Object> {
+    private final Object[] keyArray;
+    private final Object[] valueArray;
+    private final int size;
+    private int writeIndex;
+
+    // Constructor for serialization (wrapping existing CustomMapLike)
+    public MapView(CustomMapLike mapLike) {
+        this.size = mapLike.size();
+        this.keyArray = mapLike.getKeyArray();
+        this.valueArray = mapLike.getValueArray();
+    }
+
+    // Constructor for deserialization
+    public MapView(int size) {
+        this.size = size;
+        this.keyArray = new Object[size];
+        this.valueArray = new Object[size];
+    }
+
+    @Override
+    public Set<Entry<Object, Object>> entrySet() {
+        return new AbstractSet<Entry<Object, Object>>() {
+            @Override
+            public Iterator<Entry<Object, Object>> iterator() {
+                return new Iterator<Entry<Object, Object>>() {
+                    private int index = 0;
+
+                    @Override
+                    public boolean hasNext() {
+                        return index < size;
+                    }
+
+                    @Override
+                    public Entry<Object, Object> next() {
+                        if (!hasNext()) {
+                            throw new NoSuchElementException();
+                        }
+                        final int currentIndex = index++;
+                        return new SimpleEntry<>(
+                            keyArray[currentIndex],
+                            valueArray[currentIndex]
+                        );
+                    }
+                };
+            }
+
+            @Override
+            public int size() {
+                return size;
+            }
+        };
+    }
+
+    @Override
+    public Object put(Object key, Object value) {
+        if (writeIndex >= size) {
+            throw new IllegalStateException("Map is full");
+        }
+        keyArray[writeIndex] = key;
+        valueArray[writeIndex] = value;
+        writeIndex++;
+        return null;
+    }
+
+    public Object[] getKeyArray() {
+        return keyArray;
+    }
+
+    public Object[] getValueArray() {
+        return valueArray;
+    }
+
+    public int size() {
+        return size;
+    }
+}
+
+class CustomMapLikeSerializer extends AbstractMapSerializer<CustomMapLike> {
+    public CustomMapLikeSerializer(Fory fory) {
+        super(fory, CustomMapLike.class, true);
+    }
+
+    @Override
+    public Map onMapWrite(MemoryBuffer buffer, CustomMapLike value) {
+        buffer.writeVarUint32Small7(value.size());
+        // Return a zero-copy view using the same underlying arrays
+        return new MapView(value);
+    }
+
+    @Override
+    public Map newMap(MemoryBuffer buffer) {
+        int numElements = buffer.readVarUint32Small7();
+        setNumElements(numElements);
+        // Create a view with new arrays for deserialization
+        return new MapView(numElements);
+    }
+
+    @Override
+    public CustomMapLike onMapRead(Map map) {
+        MapView view = (MapView) map;
+        // Just pass the arrays directly - no copying needed
+        return new CustomMapLike(view.getKeyArray(), view.getValueArray(), 
view.size());
+    }
+
+    @Override
+    public CustomMapLike onMapCopy(Map map) {
+        MapView view = (MapView) map;
+        // Just pass the arrays directly - no copying needed
+        return new CustomMapLike(view.getKeyArray(), view.getValueArray(), 
view.size());
+    }
+}
+```
+
+### Register Custom Serializers
+
+After implementing your custom serializer, register it with Fory:
+
+```java
+Fory fory = Fory.builder()
+    .withLanguage(Language.JAVA)
+    .build();
+
+// Register map serializer
+fory.registerSerializer(CustomMap.class, new CustomMapSerializer<>(fory, 
CustomMap.class));
+
+// Register collection serializer
+fory.registerSerializer(CustomCollection.class, new 
CustomCollectionSerializer<>(fory, CustomCollection.class));
+```
+
+Note that when implementing custom map or collection serializers:
+
+1. Always extend the appropriate base class 
(`MapSerializer`/`AbstractMapSerializer` for maps, 
`CollectionSerializer`/`AbstractCollectionSerializer` for collections)
+2. Consider the impact of `supportCodegenHook` on performance and functionality
+3. Properly handle reference tracking if needed
+4. Implement proper size management using `setNumElements` and 
`getAndClearNumElements` when `supportCodegenHook` is `true`
+
 ### Security & Class Registration
 
 `ForyBuilder#requireClassRegistration` can be used to disable class 
registration, this will allow to deserialize objects
@@ -463,13 +1253,14 @@ returned.
 ### Coping/Mapping object from one type to another type
 
 Fory support mapping object from one type to another type.
+
 > Notes:
 >
 > 1. This mapping will execute a deep copy, all mapped fields are serialized 
 > into binary and
-     deserialized from that binary to map into another type.
+> deserialized from that binary to map into another type.
 > 2. All struct types must be registered with same ID, otherwise Fory can not 
 > mapping to correct struct type.
-     > Be careful when you use `Fory#register(Class)`, because fory will 
allocate an auto-grown ID which might be
-     > inconsistent if you register classes with different order between Fory 
instance.
+> Be careful when you use `Fory#register(Class)`, because fory will allocate 
an auto-grown ID which might be
+> inconsistent if you register classes with different order between Fory 
instance.
 
 ```java
 public class StructMappingExample {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to