CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyfury

Blazingly fast multi-language serialization framework powered by JIT and zero-copy

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

custom-serializers.mddocs/

Custom Serializers

Apache Fury's extensible serializer framework allows for custom serialization logic, specialized type handling, and performance optimizations for specific use cases.

Serializer Base Classes

Serializer

Base abstract class for all custom serializers.

public abstract class Serializer<T> {
    // Core serialization methods
    public abstract void write(MemoryBuffer buffer, T value);
    public abstract T read(MemoryBuffer buffer);
    
    // Type information
    public abstract Class<T> getType();
    public short getTypeId();
    
    // Reference tracking
    public boolean needToWriteRef();
    public void setNeedToWriteRef(boolean needToWriteRef);
    
    // Cross-language support
    public boolean supportCodegenHook();
    public String codecgen(CodegenContext context);
    
    // Lifecycle methods
    public void setFury(Fury fury);
    public Fury getFury();
}

AbstractObjectSerializer

Base class for object serializers with field-based serialization.

public abstract class AbstractObjectSerializer<T> extends Serializer<T> {
    // Field-based serialization support
    protected void writeFields(MemoryBuffer buffer, T obj);
    protected T readFields(MemoryBuffer buffer, T obj);
    
    // Object creation
    protected T newInstance();
    protected T newInstance(Class<?> clazz);
    
    // Field access
    protected Object getFieldValue(Object obj, String fieldName);
    protected void setFieldValue(Object obj, String fieldName, Object value);
}

Specialized Serializers

CodegenSerializer

Base class for code-generated serializers providing optimal performance.

public abstract class CodegenSerializer<T> extends Serializer<T> {
    // Generated serialization methods
    @Override
    public void write(MemoryBuffer buffer, T value);
    @Override
    public T read(MemoryBuffer buffer);
    
    // Code generation support
    public static String genCode(Class<?> clazz, CodegenContext context);
}

CompatibleSerializer

Serializer supporting schema evolution and backward compatibility.

public abstract class CompatibleSerializer<T> extends AbstractObjectSerializer<T> {
    // Schema compatibility support
    protected void writeTypeAndObject(MemoryBuffer buffer, T obj);
    protected T readTypeAndObject(MemoryBuffer buffer);
    
    // Schema evolution
    protected boolean isSchemaCompatible(ClassDef classDef);
    protected void handleSchemaEvolution(MemoryBuffer buffer, T obj, ClassDef classDef);
}

ImmutableSerializer

Optimized serializer for immutable objects.

public abstract class ImmutableSerializer<T> extends Serializer<T> {
    // Immutable object handling
    @Override
    public final boolean needToWriteRef();
    
    // Optimized for immutable types
    protected abstract T createInstance(Object... args);
}

Collection Serializers

AbstractCollectionSerializer

Base class for collection serializers.

public abstract class AbstractCollectionSerializer<T> extends Serializer<T> {
    // Collection serialization
    protected void writeElements(MemoryBuffer buffer, Collection<?> collection);
    protected Collection<Object> readElements(MemoryBuffer buffer, Collection<Object> collection);
    
    // Collection metadata
    protected void writeCollectionInfo(MemoryBuffer buffer, Collection<?> collection);
    protected CollectionInfo readCollectionInfo(MemoryBuffer buffer);
    
    // Element handling
    protected void writeElement(MemoryBuffer buffer, Object element);
    protected Object readElement(MemoryBuffer buffer);
}

AbstractMapSerializer

Base class for map serializers.

public abstract class AbstractMapSerializer<T> extends Serializer<T> {
    // Map serialization
    protected void writeKVs(MemoryBuffer buffer, Map<?, ?> map);
    protected Map<Object, Object> readKVs(MemoryBuffer buffer, Map<Object, Object> map);
    
    // Map metadata
    protected void writeMapInfo(MemoryBuffer buffer, Map<?, ?> map);
    protected MapInfo readMapInfo(MemoryBuffer buffer);
    
    // Key-value handling
    protected void writeKV(MemoryBuffer buffer, Object key, Object value);
    protected Map.Entry<Object, Object> readKV(MemoryBuffer buffer);
}

Serializer Factory

Factory for creating and managing serializers.

public class SerializerFactory {
    // Serializer creation
    public static <T> Serializer<T> createSerializer(Fury fury, Class<T> type);
    public static <T> Serializer<T> createObjectSerializer(Fury fury, Class<T> type);
    public static <T> Serializer<T> createCompatibleSerializer(Fury fury, Class<T> type);
    
    // Built-in serializers
    public static Serializer<?> createCollectionSerializer(Fury fury, Class<?> collectionType);
    public static Serializer<?> createMapSerializer(Fury fury, Class<?> mapType);
    public static Serializer<?> createArraySerializer(Fury fury, Class<?> arrayType);
    
    // Serializer utilities
    public static boolean canCreateSerializer(Class<?> type);
    public static boolean needsCustomSerializer(Class<?> type);
}

Built-in Serializers

Primitive Serializers

public class PrimitiveSerializers {
    public static class BooleanSerializer extends Serializer<Boolean>;
    public static class ByteSerializer extends Serializer<Byte>;
    public static class ShortSerializer extends Serializer<Short>;
    public static class IntSerializer extends Serializer<Integer>;
    public static class LongSerializer extends Serializer<Long>;
    public static class FloatSerializer extends Serializer<Float>;
    public static class DoubleSerializer extends Serializer<Double>;
    public static class CharSerializer extends Serializer<Character>;
}

String Serializers

public class StringSerializer extends Serializer<String> {
    // Optimized string serialization
    @Override
    public void write(MemoryBuffer buffer, String value);
    @Override
    public String read(MemoryBuffer buffer);
    
    // String encoding options
    public void setEncoding(Charset encoding);
    public void setCompressed(boolean compressed);
}

Time Serializers

public class TimeSerializers {
    public static class DateSerializer extends Serializer<Date>;
    public static class TimestampSerializer extends Serializer<Timestamp>;
    public static class InstantSerializer extends Serializer<Instant>;
    public static class LocalDateSerializer extends Serializer<LocalDate>;
    public static class LocalTimeSerializer extends Serializer<LocalTime>;
    public static class LocalDateTimeSerializer extends Serializer<LocalDateTime>;
}

Usage Examples

Creating Custom Serializers

import org.apache.fury.Fury;
import org.apache.fury.memory.MemoryBuffer;
import org.apache.fury.serializer.Serializer;

// Custom class to serialize
public class Point {
    private double x, y;
    
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
    
    // getters and setters...
}

// Custom serializer
public class PointSerializer extends Serializer<Point> {
    @Override
    public void write(MemoryBuffer buffer, Point point) {
        buffer.writeDouble(point.getX());
        buffer.writeDouble(point.getY());
    }
    
    @Override
    public Point read(MemoryBuffer buffer) {
        double x = buffer.readDouble();
        double y = buffer.readDouble();
        return new Point(x, y);
    }
    
    @Override
    public Class<Point> getType() {
        return Point.class;
    }
}

// Register and use
Fury fury = Fury.builder().build();
fury.registerSerializer(Point.class, new PointSerializer());

// Serialization works with custom serializer
Point point = new Point(1.5, 2.5);
byte[] bytes = fury.serialize(point);
Point restored = (Point) fury.deserialize(bytes);

Complex Object Serializer

import org.apache.fury.serializer.AbstractObjectSerializer;

public class UserSerializer extends AbstractObjectSerializer<User> {
    @Override
    public void write(MemoryBuffer buffer, User user) {
        // Write fields in specific order
        buffer.writeString(user.getName());
        buffer.writeInt(user.getAge());
        buffer.writeBoolean(user.isActive());
        
        // Handle optional fields
        if (user.getEmail() != null) {
            buffer.writeBoolean(true);
            buffer.writeString(user.getEmail());
        } else {
            buffer.writeBoolean(false);
        }
        
        // Serialize nested objects
        getFury().writeRef(buffer, user.getAddress());
    }
    
    @Override
    public User read(MemoryBuffer buffer) {
        String name = buffer.readString();
        int age = buffer.readInt();
        boolean active = buffer.readBoolean();
        
        // Handle optional fields
        String email = null;
        if (buffer.readBoolean()) {
            email = buffer.readString();
        }
        
        // Deserialize nested objects
        Address address = (Address) getFury().readRef(buffer);
        
        return new User(name, age, active, email, address);
    }
    
    @Override
    public Class<User> getType() {
        return User.class;
    }
}

Collection Serializer

import org.apache.fury.serializer.AbstractCollectionSerializer;

public class CustomListSerializer extends AbstractCollectionSerializer<CustomList> {
    @Override
    public void write(MemoryBuffer buffer, CustomList list) {
        // Write size
        buffer.writeInt(list.size());
        
        // Write custom metadata
        buffer.writeString(list.getName());
        buffer.writeBoolean(list.isSorted());
        
        // Write elements
        for (Object element : list) {
            getFury().writeRef(buffer, element);
        }
    }
    
    @Override
    public CustomList read(MemoryBuffer buffer) {
        int size = buffer.readInt();
        String name = buffer.readString();
        boolean sorted = buffer.readBoolean();
        
        CustomList list = new CustomList(name, sorted);
        
        // Read elements
        for (int i = 0; i < size; i++) {
            Object element = getFury().readRef(buffer);
            list.add(element);
        }
        
        return list;
    }
    
    @Override
    public Class<CustomList> getType() {
        return CustomList.class;
    }
}

Immutable Object Serializer

import org.apache.fury.serializer.ImmutableSerializer;

public class ImmutablePersonSerializer extends ImmutableSerializer<ImmutablePerson> {
    @Override
    public void write(MemoryBuffer buffer, ImmutablePerson person) {
        buffer.writeString(person.getName());
        buffer.writeInt(person.getAge());
        buffer.writeString(person.getEmail());
    }
    
    @Override
    public ImmutablePerson read(MemoryBuffer buffer) {
        String name = buffer.readString();
        int age = buffer.readInt();
        String email = buffer.readString();
        
        return ImmutablePerson.builder()
            .name(name)
            .age(age)
            .email(email)
            .build();
    }
    
    @Override
    protected ImmutablePerson createInstance(Object... args) {
        return ImmutablePerson.builder()
            .name((String) args[0])
            .age((Integer) args[1])
            .email((String) args[2])
            .build();
    }
    
    @Override
    public Class<ImmutablePerson> getType() {
        return ImmutablePerson.class;
    }
}

Enum Serializer

public class CustomEnumSerializer extends Serializer<MyEnum> {
    @Override
    public void write(MemoryBuffer buffer, MyEnum value) {
        // Write enum ordinal for efficiency
        buffer.writeInt(value.ordinal());
    }
    
    @Override
    public MyEnum read(MemoryBuffer buffer) {
        int ordinal = buffer.readInt();
        return MyEnum.values()[ordinal];
    }
    
    @Override
    public Class<MyEnum> getType() {
        return MyEnum.class;
    }
    
    // Disable reference tracking for enums
    @Override
    public boolean needToWriteRef() {
        return false;
    }
}

Versioned Serializer

import org.apache.fury.serializer.CompatibleSerializer;

public class VersionedUserSerializer extends CompatibleSerializer<User> {
    private static final int VERSION = 2;
    
    @Override
    public void write(MemoryBuffer buffer, User user) {
        // Write version first
        buffer.writeInt(VERSION);
        
        // Write fields based on current version
        buffer.writeString(user.getName());
        buffer.writeInt(user.getAge());
        
        if (VERSION >= 2) {
            buffer.writeString(user.getEmail()); // Added in version 2
        }
    }
    
    @Override
    public User read(MemoryBuffer buffer) {
        int version = buffer.readInt();
        
        String name = buffer.readString();
        int age = buffer.readInt();
        
        String email = null;
        if (version >= 2) {
            email = buffer.readString();
        }
        
        return new User(name, age, email);
    }
    
    @Override
    public Class<User> getType() {
        return User.class;
    }
}

Serializer Registration

public class SerializerRegistration {
    public static void registerCustomSerializers(Fury fury) {
        // Register individual serializers
        fury.registerSerializer(Point.class, new PointSerializer());
        fury.registerSerializer(User.class, new UserSerializer());
        
        // Register serializer class (instantiated by Fury)
        fury.registerSerializer(CustomList.class, CustomListSerializer.class);
        
        // Register with custom factory
        fury.registerSerializer(ImmutablePerson.class, 
            (f) -> new ImmutablePersonSerializer());
    }
}

Advanced Patterns

Conditional Serialization

public class ConditionalSerializer extends Serializer<ConditionalObject> {
    @Override
    public void write(MemoryBuffer buffer, ConditionalObject obj) {
        // Write discriminant
        buffer.writeByte(obj.getType().ordinal());
        
        // Conditional serialization based on type
        switch (obj.getType()) {
            case SIMPLE:
                buffer.writeString(obj.getSimpleValue());
                break;
            case COMPLEX:
                getFury().writeRef(buffer, obj.getComplexValue());
                break;
            case OPTIMIZED:
                writeOptimizedFormat(buffer, obj);
                break;
        }
    }
    
    @Override
    public ConditionalObject read(MemoryBuffer buffer) {
        ObjectType type = ObjectType.values()[buffer.readByte()];
        
        switch (type) {
            case SIMPLE:
                return new ConditionalObject(type, buffer.readString());
            case COMPLEX:
                return new ConditionalObject(type, getFury().readRef(buffer));
            case OPTIMIZED:
                return readOptimizedFormat(buffer);
            default:
                throw new IllegalStateException("Unknown type: " + type);
        }
    }
}

Performance Optimizations

public class OptimizedSerializer extends Serializer<LargeObject> {
    // Cache field accessors for performance
    private final FieldAccessor[] fieldAccessors;
    
    public OptimizedSerializer() {
        Field[] fields = ReflectionUtils.getFields(LargeObject.class);
        fieldAccessors = new FieldAccessor[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldAccessors[i] = FieldAccessor.createUnsafeAccessor(fields[i]);
        }
    }
    
    @Override
    public void write(MemoryBuffer buffer, LargeObject obj) {
        // Use cached accessors for maximum performance
        for (FieldAccessor accessor : fieldAccessors) {
            Class<?> type = accessor.getType();
            if (type == int.class) {
                buffer.writeInt(accessor.getInt(obj));
            } else if (type == String.class) {
                buffer.writeString((String) accessor.get(obj));
            }
            // ... handle other types
        }
    }
    
    @Override
    public Class<LargeObject> getType() {
        return LargeObject.class;
    }
}

Performance Considerations

Serializer Design

  • Minimize Object Creation: Reuse objects and avoid unnecessary allocations
  • Use Primitive Operations: Access primitive fields directly for better performance
  • Cache Metadata: Cache field accessors and type information
  • Optimize Hot Paths: Focus on frequently serialized types

Registration Strategy

  • Register Early: Register serializers during application startup
  • Use Type IDs: Assign explicit type IDs for better performance
  • Batch Registration: Register related serializers together
  • Monitor Performance: Profile serialization hotspots

Memory Efficiency

  • Compact Formats: Use efficient binary representations
  • Avoid Boxing: Work with primitives when possible
  • Reference Management: Carefully manage reference tracking needs
  • Buffer Usage: Minimize buffer allocations and copies

Install with Tessl CLI

npx tessl i tessl/pypi-pyfury

docs

core-serialization.md

custom-serializers.md

index.md

memory-management.md

type-system.md

tile.json