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 455628477 perf(go): optimize go struct fields serialization perf 
(#3120)
455628477 is described below

commit 455628477ba6bef2d565b961db7077b180fd26b3
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Jan 11 11:28:20 2026 +0800

    perf(go): optimize go struct fields serialization perf (#3120)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    Fix performance regression introduced in #3113
    
    ## Related issues
    
    #3113
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 go/fory/buffer.go     |  41 +++++++++
 go/fory/field_info.go | 117 ++++++++++++++++---------
 go/fory/primitive.go  |  34 ++++++++
 go/fory/struct.go     | 238 +++++++++++++++++++++++++-------------------------
 go/fory/types.go      |  52 ++++++-----
 5 files changed, 296 insertions(+), 186 deletions(-)

diff --git a/go/fory/buffer.go b/go/fory/buffer.go
index 13ec17f4a..74e4498d7 100644
--- a/go/fory/buffer.go
+++ b/go/fory/buffer.go
@@ -1539,6 +1539,47 @@ func (b *ByteBuffer) UnsafePutInt64(offset int, value 
int64) int {
        return 8
 }
 
+// UnsafePutTaggedInt64 writes int64 using tagged encoding at the given offset.
+// Caller must have ensured capacity (9 bytes max).
+// Returns the number of bytes written (4 or 9).
+//
+//go:inline
+func (b *ByteBuffer) UnsafePutTaggedInt64(offset int, value int64) int {
+       const halfMinIntValue int64 = -1073741824 // INT32_MIN / 2
+       const halfMaxIntValue int64 = 1073741823  // INT32_MAX / 2
+       if value >= halfMinIntValue && value <= halfMaxIntValue {
+               binary.LittleEndian.PutUint32(b.data[offset:], 
uint32(int32(value)<<1))
+               return 4
+       }
+       b.data[offset] = 0b1
+       if isLittleEndian {
+               *(*int64)(unsafe.Pointer(&b.data[offset+1])) = value
+       } else {
+               binary.LittleEndian.PutUint64(b.data[offset+1:], uint64(value))
+       }
+       return 9
+}
+
+// UnsafePutTaggedUint64 writes uint64 using tagged encoding at the given 
offset.
+// Caller must have ensured capacity (9 bytes max).
+// Returns the number of bytes written (4 or 9).
+//
+//go:inline
+func (b *ByteBuffer) UnsafePutTaggedUint64(offset int, value uint64) int {
+       const maxSmallValue uint64 = 0x7fffffff // INT32_MAX as u64
+       if value <= maxSmallValue {
+               binary.LittleEndian.PutUint32(b.data[offset:], uint32(value)<<1)
+               return 4
+       }
+       b.data[offset] = 0b1
+       if isLittleEndian {
+               *(*uint64)(unsafe.Pointer(&b.data[offset+1])) = value
+       } else {
+               binary.LittleEndian.PutUint64(b.data[offset+1:], value)
+       }
+       return 9
+}
+
 // ReadVaruint32Small7 reads a varuint32 in small-7 format with error checking
 func (b *ByteBuffer) ReadVaruint32Small7(err *Error) uint32 {
        if b.readerIndex >= len(b.data) {
diff --git a/go/fory/field_info.go b/go/fory/field_info.go
index cac8ae446..7739894ec 100644
--- a/go/fory/field_info.go
+++ b/go/fory/field_info.go
@@ -24,27 +24,31 @@ import (
        "strings"
 )
 
-// FieldInfo stores field metadata computed ENTIRELY at init time.
-// All flags and decisions are pre-computed to eliminate runtime checks.
-type FieldInfo struct {
+// PrimitiveFieldInfo contains only the fields needed for hot primitive 
serialization loops.
+// This minimal struct improves cache efficiency during iteration.
+// Size: 16 bytes (vs full FieldInfo)
+type PrimitiveFieldInfo struct {
+       Offset      uintptr    // Field offset for unsafe access
+       DispatchId  DispatchId // Type dispatch ID
+       WriteOffset uint8      // Offset within fixed-fields buffer (0-255, 
sufficient for fixed primitives)
+}
+
+// FieldMeta contains cold/rarely-accessed field metadata.
+// Accessed via pointer from FieldInfo to keep FieldInfo small for cache 
efficiency.
+type FieldMeta struct {
        Name       string
-       Offset     uintptr
        Type       reflect.Type
-       DispatchId DispatchId
        TypeId     TypeId // Fory type ID for the serializer
-       Serializer Serializer
        Nullable   bool
        FieldIndex int      // -1 if field doesn't exist in current struct (for 
compatible mode)
        FieldDef   FieldDef // original FieldDef from remote TypeDef (for 
compatible mode skip)
 
-       // Pre-computed sizes and offsets (for fixed primitives)
-       FixedSize   int // 0 if not fixed-size, else 1/2/4/8
-       WriteOffset int // Offset within fixed-fields buffer region (sum of 
preceding field sizes)
+       // Pre-computed sizes (for fixed primitives)
+       FixedSize int // 0 if not fixed-size, else 1/2/4/8
 
        // Pre-computed flags for serialization (computed at init time)
-       RefMode     RefMode // ref mode for serializer.Write/Read
-       WriteType   bool    // whether to write type info (true for struct 
fields in compatible mode)
-       HasGenerics bool    // whether element types are known from TypeDef 
(for container fields)
+       WriteType   bool // whether to write type info (true for struct fields 
in compatible mode)
+       HasGenerics bool // whether element types are known from TypeDef (for 
container fields)
 
        // Tag-based configuration (from fory struct tags)
        TagID          int  // -1 = use field name, >=0 = use tag ID
@@ -53,9 +57,21 @@ type FieldInfo struct {
        TagRef         bool // The ref value from fory tag (only valid if 
TagRefSet is true)
        TagNullableSet bool // Whether nullable was explicitly set via fory tag
        TagNullable    bool // The nullable value from fory tag (only valid if 
TagNullableSet is true)
+}
 
-       // Pre-computed type flags (computed at init time to avoid runtime 
reflection)
-       IsPtr bool // True if field.Type.Kind() == reflect.Ptr
+// FieldInfo stores field metadata computed ENTIRELY at init time.
+// Hot fields are kept inline for cache efficiency, cold fields accessed via 
Meta pointer.
+type FieldInfo struct {
+       // Hot fields - accessed frequently during serialization
+       Offset      uintptr    // Field offset for unsafe access
+       DispatchId  DispatchId // Type dispatch ID
+       WriteOffset int        // Offset within fixed-fields buffer region (sum 
of preceding field sizes)
+       RefMode     RefMode    // ref mode for serializer.Write/Read
+       IsPtr       bool       // True if field.Type.Kind() == reflect.Ptr
+       Serializer  Serializer // Serializer for this field
+
+       // Cold fields - accessed less frequently
+       Meta *FieldMeta
 }
 
 // FieldGroup holds categorized and sorted fields for optimized serialization.
@@ -65,6 +81,11 @@ type FieldInfo struct {
 // - VarintFields: non-nullable varint primitives (varint32/64, var_uint32/64, 
tagged_int64/uint64)
 // - RemainingFields: all other fields (nullable primitives, strings, 
collections, structs, etc.)
 type FieldGroup struct {
+       // Primitive field slices - minimal data for fast iteration in hot loops
+       PrimitiveFixedFields  []PrimitiveFieldInfo // Minimal fixed field info 
for hot loop
+       PrimitiveVarintFields []PrimitiveFieldInfo // Minimal varint field info 
for hot loop
+
+       // Full field info for remaining fields and fallback paths
        FixedFields     []FieldInfo // Non-nullable fixed-size primitives
        VarintFields    []FieldInfo // Non-nullable varint primitives
        RemainingFields []FieldInfo // All other fields
@@ -100,19 +121,19 @@ func (g *FieldGroup) DebugPrint(typeName string) {
        for i := range g.FixedFields {
                f := &g.FixedFields[i]
                fmt.Printf("[Go]   [%d] %s -> dispatchId=%d, typeId=%d, 
size=%d, nullable=%v\n",
-                       i, f.Name, f.DispatchId, f.TypeId, f.FixedSize, 
f.Nullable)
+                       i, f.Meta.Name, f.DispatchId, f.Meta.TypeId, 
f.Meta.FixedSize, f.Meta.Nullable)
        }
        fmt.Printf("[Go] Go sorted varintFields (%d):\n", len(g.VarintFields))
        for i := range g.VarintFields {
                f := &g.VarintFields[i]
                fmt.Printf("[Go]   [%d] %s -> dispatchId=%d, typeId=%d, 
nullable=%v\n",
-                       i, f.Name, f.DispatchId, f.TypeId, f.Nullable)
+                       i, f.Meta.Name, f.DispatchId, f.Meta.TypeId, 
f.Meta.Nullable)
        }
        fmt.Printf("[Go] Go sorted remainingFields (%d):\n", 
len(g.RemainingFields))
        for i := range g.RemainingFields {
                f := &g.RemainingFields[i]
                fmt.Printf("[Go]   [%d] %s -> dispatchId=%d, typeId=%d, 
nullable=%v\n",
-                       i, f.Name, f.DispatchId, f.TypeId, f.Nullable)
+                       i, f.Meta.Name, f.DispatchId, f.Meta.TypeId, 
f.Meta.Nullable)
        }
        fmt.Printf("[Go] ===========================================\n")
 }
@@ -126,11 +147,11 @@ func GroupFields(fields []FieldInfo) FieldGroup {
        // Categorize fields
        for i := range fields {
                field := &fields[i]
-               if isFixedSizePrimitive(field.DispatchId, field.Nullable) {
+               if isFixedSizePrimitive(field.DispatchId, field.Meta.Nullable) {
                        // Non-nullable fixed-size primitives only
-                       field.FixedSize = 
getFixedSizeByDispatchId(field.DispatchId)
+                       field.Meta.FixedSize = 
getFixedSizeByDispatchId(field.DispatchId)
                        g.FixedFields = append(g.FixedFields, *field)
-               } else if isVarintPrimitive(field.DispatchId, field.Nullable) {
+               } else if isVarintPrimitive(field.DispatchId, 
field.Meta.Nullable) {
                        // Non-nullable varint primitives only
                        g.VarintFields = append(g.VarintFields, *field)
                } else {
@@ -142,19 +163,25 @@ func GroupFields(fields []FieldInfo) FieldGroup {
        // Sort fixedFields: size desc, typeId desc, name asc
        sort.SliceStable(g.FixedFields, func(i, j int) bool {
                fi, fj := &g.FixedFields[i], &g.FixedFields[j]
-               if fi.FixedSize != fj.FixedSize {
-                       return fi.FixedSize > fj.FixedSize // size descending
+               if fi.Meta.FixedSize != fj.Meta.FixedSize {
+                       return fi.Meta.FixedSize > fj.Meta.FixedSize // size 
descending
                }
-               if fi.TypeId != fj.TypeId {
-                       return fi.TypeId > fj.TypeId // typeId descending
+               if fi.Meta.TypeId != fj.Meta.TypeId {
+                       return fi.Meta.TypeId > fj.Meta.TypeId // typeId 
descending
                }
-               return fi.Name < fj.Name // name ascending
+               return fi.Meta.Name < fj.Meta.Name // name ascending
        })
 
-       // Compute WriteOffset after sorting
+       // Compute WriteOffset after sorting and build primitive field slice
+       g.PrimitiveFixedFields = make([]PrimitiveFieldInfo, len(g.FixedFields))
        for i := range g.FixedFields {
                g.FixedFields[i].WriteOffset = g.FixedSize
-               g.FixedSize += g.FixedFields[i].FixedSize
+               g.PrimitiveFixedFields[i] = PrimitiveFieldInfo{
+                       Offset:      g.FixedFields[i].Offset,
+                       DispatchId:  g.FixedFields[i].DispatchId,
+                       WriteOffset: uint8(g.FixedSize),
+               }
+               g.FixedSize += g.FixedFields[i].Meta.FixedSize
        }
 
        // Sort varintFields: underlying type size desc, typeId desc, name asc
@@ -166,15 +193,21 @@ func GroupFields(fields []FieldInfo) FieldGroup {
                if sizeI != sizeJ {
                        return sizeI > sizeJ // size descending
                }
-               if fi.TypeId != fj.TypeId {
-                       return fi.TypeId > fj.TypeId // typeId descending
+               if fi.Meta.TypeId != fj.Meta.TypeId {
+                       return fi.Meta.TypeId > fj.Meta.TypeId // typeId 
descending
                }
-               return fi.Name < fj.Name // name ascending
+               return fi.Meta.Name < fj.Meta.Name // name ascending
        })
 
-       // Compute maxVarintSize
+       // Compute maxVarintSize and build primitive varint field slice
+       g.PrimitiveVarintFields = make([]PrimitiveFieldInfo, 
len(g.VarintFields))
        for i := range g.VarintFields {
                g.MaxVarintSize += 
getVarintMaxSizeByDispatchId(g.VarintFields[i].DispatchId)
+               g.PrimitiveVarintFields[i] = PrimitiveFieldInfo{
+                       Offset:     g.VarintFields[i].Offset,
+                       DispatchId: g.VarintFields[i].DispatchId,
+                       // WriteOffset not used for varint fields (variable 
length)
+               }
        }
 
        // Sort remainingFields: nullable primitives first (by 
primitiveComparator),
@@ -192,8 +225,8 @@ func GroupFields(fields []FieldInfo) FieldGroup {
                // Within other internal types category (STRING, BINARY, LIST, 
SET, MAP),
                // sort by typeId then by sort key (tagID if available, 
otherwise name).
                if catI == 1 {
-                       if fi.TypeId != fj.TypeId {
-                               return fi.TypeId < fj.TypeId
+                       if fi.Meta.TypeId != fj.Meta.TypeId {
+                               return fi.Meta.TypeId < fj.Meta.TypeId
                        }
                        return getFieldSortKey(fi) < getFieldSortKey(fj)
                }
@@ -215,7 +248,7 @@ func fieldHasNonPrimitiveSerializer(field *FieldInfo) bool {
        // all require special serialization and should not use the primitive 
fast path
        // Note: ENUM uses unsigned Varuint32Small7 for ordinals, not signed 
zigzag varint
        // Use internal type ID (low 8 bits) since registered types have 
composite TypeIds like (userID << 8) | internalID
-       internalTypeId := TypeId(field.TypeId & 0xFF)
+       internalTypeId := TypeId(field.Meta.TypeId & 0xFF)
        switch internalTypeId {
        case ENUM, NAMED_ENUM, NAMED_STRUCT, NAMED_COMPATIBLE_STRUCT, NAMED_EXT:
                return true
@@ -229,7 +262,7 @@ func isEnumField(field *FieldInfo) bool {
        if field.Serializer == nil {
                return false
        }
-       internalTypeId := field.TypeId & 0xFF
+       internalTypeId := field.Meta.TypeId & 0xFF
        return internalTypeId == ENUM || internalTypeId == NAMED_ENUM
 }
 
@@ -241,7 +274,7 @@ func getFieldCategory(field *FieldInfo) int {
        if isNullableFixedSizePrimitive(field.DispatchId) || 
isNullableVarintPrimitive(field.DispatchId) {
                return 0
        }
-       internalId := field.TypeId & 0xFF
+       internalId := field.Meta.TypeId & 0xFF
        switch TypeId(internalId) {
        case STRING, BINARY, LIST, SET, MAP:
                // Internal types: sorted by typeId, then name
@@ -267,10 +300,10 @@ func comparePrimitiveFields(fi, fj *FieldInfo) bool {
        if sizeI != sizeJ {
                return sizeI > sizeJ // size descending
        }
-       if fi.TypeId != fj.TypeId {
-               return fi.TypeId > fj.TypeId // typeId descending
+       if fi.Meta.TypeId != fj.Meta.TypeId {
+               return fi.Meta.TypeId > fj.Meta.TypeId // typeId descending
        }
-       return fi.Name < fj.Name // name ascending
+       return fi.Meta.Name < fj.Meta.Name // name ascending
 }
 
 // getNullableFixedSize returns the fixed size for nullable fixed primitives
@@ -532,10 +565,10 @@ func (t triple) getSortKey() string {
 // If TagID >= 0, returns the tag ID as string (for tag-based sorting).
 // Otherwise returns the field name (which is already snake_case).
 func getFieldSortKey(f *FieldInfo) string {
-       if f.TagID >= 0 {
-               return fmt.Sprintf("%d", f.TagID)
+       if f.Meta.TagID >= 0 {
+               return fmt.Sprintf("%d", f.Meta.TagID)
        }
-       return f.Name
+       return f.Meta.Name
 }
 
 // sortFields sorts fields with nullable information to match Java's field 
ordering.
diff --git a/go/fory/primitive.go b/go/fory/primitive.go
index b0bf96767..d8978b88a 100644
--- a/go/fory/primitive.go
+++ b/go/fory/primitive.go
@@ -19,6 +19,7 @@ package fory
 
 import (
        "reflect"
+       "unsafe"
 )
 
 // ============================================================================
@@ -563,3 +564,36 @@ func (s float64Serializer) Read(ctx *ReadContext, refMode 
RefMode, readType bool
 func (s float64Serializer) ReadWithTypeInfo(ctx *ReadContext, refMode RefMode, 
typeInfo *TypeInfo, value reflect.Value) {
        s.Read(ctx, refMode, false, false, value)
 }
+
+// ============================================================================
+// Notnull Pointer Helper Functions for Varint Types
+// These are used by struct serializer for the rare case of *T with 
nullable=false
+// ============================================================================
+
+// writeNotnullVarintPtrUnsafe writes a notnull pointer varint type at the 
given offset.
+// Used by struct serializer for rare notnull pointer types.
+// Returns the number of bytes written.
+//
+//go:inline
+func writeNotnullVarintPtrUnsafe(buf *ByteBuffer, offset int, fieldPtr 
unsafe.Pointer, dispatchId DispatchId) int {
+       switch dispatchId {
+       case NotnullVarint32PtrDispatchId:
+               return buf.UnsafePutVarInt32(offset, **(**int32)(fieldPtr))
+       case NotnullVarint64PtrDispatchId:
+               return buf.UnsafePutVarInt64(offset, **(**int64)(fieldPtr))
+       case NotnullIntPtrDispatchId:
+               return buf.UnsafePutVarInt64(offset, int64(**(**int)(fieldPtr)))
+       case NotnullVarUint32PtrDispatchId:
+               return buf.UnsafePutVaruint32(offset, **(**uint32)(fieldPtr))
+       case NotnullVarUint64PtrDispatchId:
+               return buf.UnsafePutVaruint64(offset, **(**uint64)(fieldPtr))
+       case NotnullUintPtrDispatchId:
+               return buf.UnsafePutVaruint64(offset, 
uint64(**(**uint)(fieldPtr)))
+       case NotnullTaggedInt64PtrDispatchId:
+               return buf.UnsafePutTaggedInt64(offset, **(**int64)(fieldPtr))
+       case NotnullTaggedUint64PtrDispatchId:
+               return buf.UnsafePutTaggedUint64(offset, **(**uint64)(fieldPtr))
+       default:
+               return 0
+       }
+}
diff --git a/go/fory/struct.go b/go/fory/struct.go
index b8f3c7765..e38f51a4e 100644
--- a/go/fory/struct.go
+++ b/go/fory/struct.go
@@ -306,27 +306,29 @@ func (s *structSerializer) initFields(typeResolver 
*TypeResolver) error {
                }
 
                fieldInfo := FieldInfo{
-                       Name:           SnakeCase(field.Name),
-                       Offset:         field.Offset,
-                       Type:           fieldType,
-                       DispatchId:     dispatchId,
-                       TypeId:         fieldTypeId,
-                       Serializer:     fieldSerializer,
-                       Nullable:       nullableFlag, // Use same logic as 
TypeDef's nullable flag for consistent ref handling
-                       FieldIndex:     i,
-                       RefMode:        refMode,
-                       WriteType:      writeType,
-                       HasGenerics:    isCollectionType(fieldTypeId), // 
Container fields have declared element types
-                       TagID:          foryTag.ID,
-                       HasForyTag:     foryTag.HasTag,
-                       TagRefSet:      foryTag.RefSet,
-                       TagRef:         foryTag.Ref,
-                       TagNullableSet: foryTag.NullableSet,
-                       TagNullable:    foryTag.Nullable,
-                       IsPtr:          fieldType.Kind() == reflect.Ptr,
+                       Offset:     field.Offset,
+                       DispatchId: dispatchId,
+                       RefMode:    refMode,
+                       IsPtr:      fieldType.Kind() == reflect.Ptr,
+                       Serializer: fieldSerializer,
+                       Meta: &FieldMeta{
+                               Name:           SnakeCase(field.Name),
+                               Type:           fieldType,
+                               TypeId:         fieldTypeId,
+                               Nullable:       nullableFlag, // Use same logic 
as TypeDef's nullable flag for consistent ref handling
+                               FieldIndex:     i,
+                               WriteType:      writeType,
+                               HasGenerics:    isCollectionType(fieldTypeId), 
// Container fields have declared element types
+                               TagID:          foryTag.ID,
+                               HasForyTag:     foryTag.HasTag,
+                               TagRefSet:      foryTag.RefSet,
+                               TagRef:         foryTag.Ref,
+                               TagNullableSet: foryTag.NullableSet,
+                               TagNullable:    foryTag.Nullable,
+                       },
                }
                fields = append(fields, fieldInfo)
-               fieldNames = append(fieldNames, fieldInfo.Name)
+               fieldNames = append(fieldNames, fieldInfo.Meta.Name)
                serializers = append(serializers, fieldSerializer)
                typeIds = append(typeIds, fieldTypeId)
                nullables = append(nullables, nullableFlag)
@@ -341,8 +343,8 @@ func (s *structSerializer) initFields(typeResolver 
*TypeResolver) error {
        }
 
        sort.SliceStable(fields, func(i, j int) bool {
-               oi, okI := order[fields[i].Name]
-               oj, okJ := order[fields[j].Name]
+               oi, okI := order[fields[i].Meta.Name]
+               oj, okJ := order[fields[j].Meta.Name]
                switch {
                case okI && okJ:
                        return oi < oj
@@ -406,19 +408,21 @@ func (s *structSerializer) 
initFieldsFromTypeDef(typeResolver *TypeResolver) err
                        }
 
                        fieldInfo := FieldInfo{
-                               Name:        def.name,
-                               Offset:      0,
-                               Type:        remoteType,
-                               DispatchId:  dispatchId,
-                               TypeId:      fieldTypeId,
-                               Serializer:  fieldSerializer,
-                               Nullable:    def.nullable, // Use remote 
nullable flag
-                               FieldIndex:  -1,           // Mark as 
non-existent field to discard data
-                               FieldDef:    def,          // Save original 
FieldDef for skipping
-                               RefMode:     refMode,
-                               WriteType:   writeType,
-                               HasGenerics: isCollectionType(fieldTypeId), // 
Container fields have declared element types
-                               IsPtr:       remoteType != nil && 
remoteType.Kind() == reflect.Ptr,
+                               Offset:     0,
+                               DispatchId: dispatchId,
+                               RefMode:    refMode,
+                               IsPtr:      remoteType != nil && 
remoteType.Kind() == reflect.Ptr,
+                               Serializer: fieldSerializer,
+                               Meta: &FieldMeta{
+                                       Name:        def.name,
+                                       Type:        remoteType,
+                                       TypeId:      fieldTypeId,
+                                       Nullable:    def.nullable, // Use 
remote nullable flag
+                                       FieldIndex:  -1,           // Mark as 
non-existent field to discard data
+                                       FieldDef:    def,          // Save 
original FieldDef for skipping
+                                       WriteType:   writeType,
+                                       HasGenerics: 
isCollectionType(fieldTypeId), // Container fields have declared element types
+                               },
                        }
                        fields = append(fields, fieldInfo)
                }
@@ -713,21 +717,23 @@ func (s *structSerializer) 
initFieldsFromTypeDef(typeResolver *TypeResolver) err
                }
 
                fieldInfo := FieldInfo{
-                       Name:        fieldName,
-                       Offset:      offset,
-                       Type:        fieldType,
-                       DispatchId:  dispatchId,
-                       TypeId:      fieldTypeId,
-                       Serializer:  fieldSerializer,
-                       Nullable:    def.nullable, // Use remote nullable flag
-                       FieldIndex:  fieldIndex,
-                       FieldDef:    def, // Save original FieldDef for skipping
-                       RefMode:     refMode,
-                       WriteType:   writeType,
-                       HasGenerics: isCollectionType(fieldTypeId), // 
Container fields have declared element types
-                       TagID:       def.tagID,
-                       HasForyTag:  def.tagID >= 0,
-                       IsPtr:       fieldType != nil && fieldType.Kind() == 
reflect.Ptr,
+                       Offset:     offset,
+                       DispatchId: dispatchId,
+                       RefMode:    refMode,
+                       IsPtr:      fieldType != nil && fieldType.Kind() == 
reflect.Ptr,
+                       Serializer: fieldSerializer,
+                       Meta: &FieldMeta{
+                               Name:        fieldName,
+                               Type:        fieldType,
+                               TypeId:      fieldTypeId,
+                               Nullable:    def.nullable, // Use remote 
nullable flag
+                               FieldIndex:  fieldIndex,
+                               FieldDef:    def, // Save original FieldDef for 
skipping
+                               WriteType:   writeType,
+                               HasGenerics: isCollectionType(fieldTypeId), // 
Container fields have declared element types
+                               TagID:       def.tagID,
+                               HasForyTag:  def.tagID >= 0,
+                       },
                }
                fields = append(fields, fieldInfo)
        }
@@ -749,7 +755,7 @@ func (s *structSerializer) 
initFieldsFromTypeDef(typeResolver *TypeResolver) err
        // When typeDefDiffers is false, we can use grouped reading for better 
performance
        s.typeDefDiffers = false
        for i, field := range fields {
-               if field.FieldIndex < 0 {
+               if field.Meta.FieldIndex < 0 {
                        // Field exists in remote TypeDef but not locally
                        s.typeDefDiffers = true
                        break
@@ -757,7 +763,7 @@ func (s *structSerializer) 
initFieldsFromTypeDef(typeResolver *TypeResolver) err
                // Check if nullable flag differs between remote and local
                // Remote nullable is stored in fieldDefs[i].nullable
                // Local nullable is determined by whether the Go field is a 
pointer type
-               if i < len(s.fieldDefs) && field.FieldIndex >= 0 {
+               if i < len(s.fieldDefs) && field.Meta.FieldIndex >= 0 {
                        remoteNullable := s.fieldDefs[i].nullable
                        // Check if local Go field is a pointer type (can be 
nil = nullable)
                        localNullable := field.IsPtr
@@ -784,7 +790,7 @@ func (s *structSerializer) computeHash() int32 {
                if field.Serializer == nil {
                        typeId = UNKNOWN
                } else {
-                       typeId = field.TypeId
+                       typeId = field.Meta.TypeId
                        // Check if this is an enum serializer (directly or 
wrapped in ptrToValueSerializer)
                        if _, ok := field.Serializer.(*enumSerializer); ok {
                                isEnumField = true
@@ -802,8 +808,8 @@ func (s *structSerializer) computeHash() int32 {
                                typeId = UNKNOWN
                        }
                        // For fixed-size arrays with primitive elements, use 
primitive array type IDs
-                       if field.Type.Kind() == reflect.Array {
-                               elemKind := field.Type.Elem().Kind()
+                       if field.Meta.Type.Kind() == reflect.Array {
+                               elemKind := field.Meta.Type.Elem().Kind()
                                switch elemKind {
                                case reflect.Int8:
                                        typeId = INT8_ARRAY
@@ -820,11 +826,11 @@ func (s *structSerializer) computeHash() int32 {
                                default:
                                        typeId = LIST
                                }
-                       } else if field.Type.Kind() == reflect.Slice {
+                       } else if field.Meta.Type.Kind() == reflect.Slice {
                                typeId = LIST
-                       } else if field.Type.Kind() == reflect.Map {
+                       } else if field.Meta.Type.Kind() == reflect.Map {
                                // map[T]bool is used to represent a Set in Go
-                               if field.Type.Elem().Kind() == reflect.Bool {
+                               if field.Meta.Type.Elem().Kind() == 
reflect.Bool {
                                        typeId = SET
                                } else {
                                        typeId = MAP
@@ -837,22 +843,22 @@ func (s *structSerializer) computeHash() int32 {
                // - Primitives are always non-nullable
                // - Can be overridden by explicit fory tag
                nullable := false // Default to nullable=false for xlang mode
-               if field.TagNullableSet {
+               if field.Meta.TagNullableSet {
                        // Use explicit tag value if set
-                       nullable = field.TagNullable
+                       nullable = field.Meta.TagNullable
                }
                // Primitives are never nullable, regardless of tag
-               if isNonNullablePrimitiveKind(field.Type.Kind()) && 
!isEnumField {
+               if isNonNullablePrimitiveKind(field.Meta.Type.Kind()) && 
!isEnumField {
                        nullable = false
                }
 
                fields = append(fields, FieldFingerprintInfo{
-                       FieldID:   field.TagID,
-                       FieldName: SnakeCase(field.Name),
+                       FieldID:   field.Meta.TagID,
+                       FieldName: SnakeCase(field.Meta.Name),
                        TypeID:    typeId,
                        // Ref is based on explicit tag annotation only, NOT 
runtime ref_tracking config
                        // This allows fingerprint to be computed at compile 
time for C++/Rust
-                       Ref:      field.TagRefSet && field.TagRef,
+                       Ref:      field.Meta.TagRefSet && field.Meta.TagRef,
                        Nullable: nullable,
                })
        }
@@ -953,9 +959,9 @@ func (s *structSerializer) WriteData(ctx *WriteContext, 
value reflect.Value) {
                baseOffset := buf.WriterIndex()
                data := buf.GetData()
 
-               for _, field := range s.fieldGroup.FixedFields {
+               for _, field := range s.fieldGroup.PrimitiveFixedFields {
                        fieldPtr := unsafe.Add(ptr, field.Offset)
-                       bufOffset := baseOffset + field.WriteOffset
+                       bufOffset := baseOffset + int(field.WriteOffset)
                        switch field.DispatchId {
                        case PrimitiveBoolDispatchId:
                                if *(*bool)(fieldPtr) {
@@ -1080,7 +1086,7 @@ func (s *structSerializer) WriteData(ctx *WriteContext, 
value reflect.Value) {
        } else if len(s.fieldGroup.FixedFields) > 0 {
                // Fallback to reflect-based access for unaddressable values
                for _, field := range s.fieldGroup.FixedFields {
-                       fieldValue := value.Field(field.FieldIndex)
+                       fieldValue := value.Field(field.Meta.FieldIndex)
                        switch field.DispatchId {
                        // Primitive types (non-pointer)
                        case PrimitiveBoolDispatchId:
@@ -1134,50 +1140,42 @@ func (s *structSerializer) WriteData(ctx *WriteContext, 
value reflect.Value) {
 
        // 
==========================================================================
        // Phase 2: Varint primitives (int32, int64, int, uint32, uint64, uint, 
tagged int64/uint64)
-       // - These are variable-length encodings that must be written 
sequentially
+       // - Reserve max size once, track offset locally, update writerIndex 
once at end
        // 
==========================================================================
-       if canUseUnsafe && len(s.fieldGroup.VarintFields) > 0 {
-               for _, field := range s.fieldGroup.VarintFields {
+       if canUseUnsafe && s.fieldGroup.MaxVarintSize > 0 {
+               buf.Reserve(s.fieldGroup.MaxVarintSize)
+               offset := buf.WriterIndex()
+
+               for _, field := range s.fieldGroup.PrimitiveVarintFields {
                        fieldPtr := unsafe.Add(ptr, field.Offset)
                        switch field.DispatchId {
                        case PrimitiveVarint32DispatchId:
-                               buf.WriteVarint32(*(*int32)(fieldPtr))
-                       case NotnullVarint32PtrDispatchId:
-                               buf.WriteVarint32(**(**int32)(fieldPtr))
+                               offset += buf.UnsafePutVarInt32(offset, 
*(*int32)(fieldPtr))
                        case PrimitiveVarint64DispatchId:
-                               buf.WriteVarint64(*(*int64)(fieldPtr))
-                       case NotnullVarint64PtrDispatchId:
-                               buf.WriteVarint64(**(**int64)(fieldPtr))
+                               offset += buf.UnsafePutVarInt64(offset, 
*(*int64)(fieldPtr))
                        case PrimitiveIntDispatchId:
-                               buf.WriteVarint64(int64(*(*int)(fieldPtr)))
-                       case NotnullIntPtrDispatchId:
-                               buf.WriteVarint64(int64(**(**int)(fieldPtr)))
+                               offset += buf.UnsafePutVarInt64(offset, 
int64(*(*int)(fieldPtr)))
                        case PrimitiveVarUint32DispatchId:
-                               buf.WriteVaruint32(*(*uint32)(fieldPtr))
-                       case NotnullVarUint32PtrDispatchId:
-                               buf.WriteVaruint32(**(**uint32)(fieldPtr))
+                               offset += buf.UnsafePutVaruint32(offset, 
*(*uint32)(fieldPtr))
                        case PrimitiveVarUint64DispatchId:
-                               buf.WriteVaruint64(*(*uint64)(fieldPtr))
-                       case NotnullVarUint64PtrDispatchId:
-                               buf.WriteVaruint64(**(**uint64)(fieldPtr))
+                               offset += buf.UnsafePutVaruint64(offset, 
*(*uint64)(fieldPtr))
                        case PrimitiveUintDispatchId:
-                               buf.WriteVaruint64(uint64(*(*uint)(fieldPtr)))
-                       case NotnullUintPtrDispatchId:
-                               buf.WriteVaruint64(uint64(**(**uint)(fieldPtr)))
+                               offset += buf.UnsafePutVaruint64(offset, 
uint64(*(*uint)(fieldPtr)))
                        case PrimitiveTaggedInt64DispatchId:
-                               buf.WriteTaggedInt64(*(*int64)(fieldPtr))
-                       case NotnullTaggedInt64PtrDispatchId:
-                               buf.WriteTaggedInt64(**(**int64)(fieldPtr))
+                               offset += buf.UnsafePutTaggedInt64(offset, 
*(*int64)(fieldPtr))
                        case PrimitiveTaggedUint64DispatchId:
-                               buf.WriteTaggedUint64(*(*uint64)(fieldPtr))
-                       case NotnullTaggedUint64PtrDispatchId:
-                               buf.WriteTaggedUint64(**(**uint64)(fieldPtr))
+                               offset += buf.UnsafePutTaggedUint64(offset, 
*(*uint64)(fieldPtr))
+                       default:
+                               // Notnull pointer types (rare case - pointers 
with nullable=false tag)
+                               offset += writeNotnullVarintPtrUnsafe(buf, 
offset, fieldPtr, field.DispatchId)
                        }
                }
+               // Update writer index ONCE after all varint fields
+               buf.SetWriterIndex(offset)
        } else if len(s.fieldGroup.VarintFields) > 0 {
                // Slow path for non-addressable values: use reflection
                for _, field := range s.fieldGroup.VarintFields {
-                       fieldValue := value.Field(field.FieldIndex)
+                       fieldValue := value.Field(field.Meta.FieldIndex)
                        switch field.DispatchId {
                        // Primitive types (non-pointer)
                        case PrimitiveVarint32DispatchId:
@@ -1270,7 +1268,7 @@ func (s *structSerializer) writeRemainingField(ctx 
*WriteContext, ptr unsafe.Poi
                        return
                case EnumDispatchId:
                        // Enums don't track refs - always use fast path
-                       writeEnumField(ctx, field, 
value.Field(field.FieldIndex))
+                       writeEnumField(ctx, field, 
value.Field(field.Meta.FieldIndex))
                        return
                case StringSliceDispatchId:
                        if field.RefMode == RefModeTracking {
@@ -1541,7 +1539,7 @@ func (s *structSerializer) writeRemainingField(ctx 
*WriteContext, ptr unsafe.Poi
        }
 
        // Slow path: use reflection for non-addressable values
-       fieldValue := value.Field(field.FieldIndex)
+       fieldValue := value.Field(field.Meta.FieldIndex)
 
        // Handle nullable types via reflection when ptr is nil 
(non-addressable)
        switch field.DispatchId {
@@ -1685,7 +1683,7 @@ func (s *structSerializer) writeRemainingField(ctx 
*WriteContext, ptr unsafe.Poi
 
        // Fall back to serializer for other types
        if field.Serializer != nil {
-               field.Serializer.Write(ctx, field.RefMode, field.WriteType, 
field.HasGenerics, fieldValue)
+               field.Serializer.Write(ctx, field.RefMode, 
field.Meta.WriteType, field.Meta.HasGenerics, fieldValue)
        } else {
                ctx.WriteValue(fieldValue, RefModeTracking, true)
        }
@@ -1795,9 +1793,9 @@ func (s *structSerializer) ReadData(ctx *ReadContext, 
type_ reflect.Type, value
                baseOffset := buf.ReaderIndex()
                data := buf.GetData()
 
-               for _, field := range s.fieldGroup.FixedFields {
+               for _, field := range s.fieldGroup.PrimitiveFixedFields {
                        fieldPtr := unsafe.Add(ptr, field.Offset)
-                       bufOffset := baseOffset + field.WriteOffset
+                       bufOffset := baseOffset + int(field.WriteOffset)
                        switch field.DispatchId {
                        case PrimitiveBoolDispatchId:
                                *(*bool)(fieldPtr) = data[bufOffset] != 0
@@ -1938,9 +1936,9 @@ func (s *structSerializer) ReadData(ctx *ReadContext, 
type_ reflect.Type, value
 
        // Phase 2: Varint primitives (must read sequentially - variable length)
        // Note: For tagged int64/uint64, we can't use unsafe reads because 
they need bounds checking
-       if len(s.fieldGroup.VarintFields) > 0 {
+       if len(s.fieldGroup.PrimitiveVarintFields) > 0 {
                err := ctx.Err()
-               for _, field := range s.fieldGroup.VarintFields {
+               for _, field := range s.fieldGroup.PrimitiveVarintFields {
                        fieldPtr := unsafe.Add(ptr, field.Offset)
                        switch field.DispatchId {
                        case PrimitiveVarint32DispatchId:
@@ -2050,7 +2048,7 @@ func (s *structSerializer) readRemainingField(ctx 
*ReadContext, ptr unsafe.Point
                        return
                case EnumDispatchId:
                        // Enums don't track refs - always use fast path
-                       fieldValue := value.Field(field.FieldIndex)
+                       fieldValue := value.Field(field.Meta.FieldIndex)
                        readEnumField(ctx, field, fieldValue)
                        return
                case StringSliceDispatchId:
@@ -2326,9 +2324,9 @@ func (s *structSerializer) readRemainingField(ctx 
*ReadContext, ptr unsafe.Point
        }
 
        // Slow path for RefModeTracking cases that break from the switch above
-       fieldValue := value.Field(field.FieldIndex)
+       fieldValue := value.Field(field.Meta.FieldIndex)
        if field.Serializer != nil {
-               field.Serializer.Read(ctx, field.RefMode, field.WriteType, 
field.HasGenerics, fieldValue)
+               field.Serializer.Read(ctx, field.RefMode, field.Meta.WriteType, 
field.Meta.HasGenerics, fieldValue)
        } else {
                ctx.ReadValue(fieldValue, RefModeTracking, true)
        }
@@ -2343,7 +2341,7 @@ func (s *structSerializer) readFieldsInOrder(ctx 
*ReadContext, value reflect.Val
        err := ctx.Err()
        for i := range s.fields {
                field := &s.fields[i]
-               if field.FieldIndex < 0 {
+               if field.Meta.FieldIndex < 0 {
                        s.skipField(ctx, field)
                        if ctx.HasError() {
                                return
@@ -2352,7 +2350,7 @@ func (s *structSerializer) readFieldsInOrder(ctx 
*ReadContext, value reflect.Val
                }
 
                // Fast path for fixed-size primitive types (no ref flag from 
remote schema)
-               if isFixedSizePrimitive(field.DispatchId, field.Nullable) {
+               if isFixedSizePrimitive(field.DispatchId, field.Meta.Nullable) {
                        fieldPtr := unsafe.Add(ptr, field.Offset)
                        switch field.DispatchId {
                        // PrimitiveXxxDispatchId: local field is non-pointer 
type
@@ -2428,7 +2426,7 @@ func (s *structSerializer) readFieldsInOrder(ctx 
*ReadContext, value reflect.Val
                }
 
                // Fast path for varint primitive types (no ref flag from 
remote schema)
-               if isVarintPrimitive(field.DispatchId, field.Nullable) && 
!fieldHasNonPrimitiveSerializer(field) {
+               if isVarintPrimitive(field.DispatchId, field.Meta.Nullable) && 
!fieldHasNonPrimitiveSerializer(field) {
                        fieldPtr := unsafe.Add(ptr, field.Offset)
                        switch field.DispatchId {
                        // PrimitiveXxxDispatchId: local field is non-pointer 
type
@@ -2486,7 +2484,7 @@ func (s *structSerializer) readFieldsInOrder(ctx 
*ReadContext, value reflect.Val
                }
 
                // Get field value for nullable primitives and non-primitives
-               fieldValue := value.Field(field.FieldIndex)
+               fieldValue := value.Field(field.Meta.FieldIndex)
 
                // Handle nullable fixed-size primitives (read ref flag + fixed 
bytes)
                // These have Nullable=true but use fixed encoding, not varint
@@ -2657,7 +2655,7 @@ func (s *structSerializer) readFieldsInOrder(ctx 
*ReadContext, value reflect.Val
                // Slow path for non-primitives (all need ref flag per xlang 
spec)
                if field.Serializer != nil {
                        // Use pre-computed RefMode and WriteType from field 
initialization
-                       field.Serializer.Read(ctx, field.RefMode, 
field.WriteType, field.HasGenerics, fieldValue)
+                       field.Serializer.Read(ctx, field.RefMode, 
field.Meta.WriteType, field.Meta.HasGenerics, fieldValue)
                } else {
                        ctx.ReadValue(fieldValue, RefModeTracking, true)
                }
@@ -2667,20 +2665,20 @@ func (s *structSerializer) readFieldsInOrder(ctx 
*ReadContext, value reflect.Val
 // skipField skips a field that doesn't exist or is incompatible
 // Uses context error state for deferred error checking.
 func (s *structSerializer) skipField(ctx *ReadContext, field *FieldInfo) {
-       if field.FieldDef.name != "" {
-               fieldDefIsStructType := 
isStructFieldType(field.FieldDef.fieldType)
+       if field.Meta.FieldDef.name != "" {
+               fieldDefIsStructType := 
isStructFieldType(field.Meta.FieldDef.fieldType)
                // Use FieldDef's trackingRef and nullable to determine if ref 
flag was written by Java
                // Java writes ref flag based on its FieldDef, not Go's field 
type
-               readRefFlag := field.FieldDef.trackingRef || 
field.FieldDef.nullable
-               SkipFieldValueWithTypeFlag(ctx, field.FieldDef, readRefFlag, 
ctx.Compatible() && fieldDefIsStructType)
+               readRefFlag := field.Meta.FieldDef.trackingRef || 
field.Meta.FieldDef.nullable
+               SkipFieldValueWithTypeFlag(ctx, field.Meta.FieldDef, 
readRefFlag, ctx.Compatible() && fieldDefIsStructType)
                return
        }
        // No FieldDef available, read into temp value
-       tempValue := reflect.New(field.Type).Elem()
+       tempValue := reflect.New(field.Meta.Type).Elem()
        if field.Serializer != nil {
-               readType := ctx.Compatible() && isStructField(field.Type)
+               readType := ctx.Compatible() && isStructField(field.Meta.Type)
                refMode := RefModeNone
-               if field.Nullable {
+               if field.Meta.Nullable {
                        refMode = RefModeTracking
                }
                field.Serializer.Read(ctx, refMode, readType, false, tempValue)
@@ -2712,7 +2710,7 @@ func writeEnumField(ctx *WriteContext, field *FieldInfo, 
fieldValue reflect.Valu
                if fieldValue.IsNil() {
                        // RefModeNone but nil pointer - this is a protocol 
error in schema-consistent mode
                        // Write zero value as fallback
-                       targetValue = reflect.Zero(field.Type.Elem())
+                       targetValue = reflect.Zero(field.Meta.Type.Elem())
                } else {
                        targetValue = fieldValue.Elem()
                }
@@ -2750,7 +2748,7 @@ func readEnumField(ctx *ReadContext, field *FieldInfo, 
fieldValue reflect.Value)
        // For pointer enum fields, allocate a new value
        targetValue := fieldValue
        if isPointer {
-               newVal := reflect.New(field.Type.Elem())
+               newVal := reflect.New(field.Meta.Type.Elem())
                fieldValue.Set(newVal)
                targetValue = newVal.Elem()
        }
@@ -2758,9 +2756,9 @@ func readEnumField(ctx *ReadContext, field *FieldInfo, 
fieldValue reflect.Value)
        // For pointer enum fields, the serializer is ptrToValueSerializer 
wrapping enumSerializer.
        // We need to call the inner enumSerializer directly with the 
dereferenced value.
        if ptrSer, ok := field.Serializer.(*ptrToValueSerializer); ok {
-               ptrSer.valueSerializer.ReadData(ctx, field.Type.Elem(), 
targetValue)
+               ptrSer.valueSerializer.ReadData(ctx, field.Meta.Type.Elem(), 
targetValue)
        } else {
-               field.Serializer.ReadData(ctx, field.Type, targetValue)
+               field.Serializer.ReadData(ctx, field.Meta.Type, targetValue)
        }
 }
 
diff --git a/go/fory/types.go b/go/fory/types.go
index a8edfc6b9..0da3b7dfc 100644
--- a/go/fory/types.go
+++ b/go/fory/types.go
@@ -279,28 +279,32 @@ type DispatchId uint8
 const (
        UnknownDispatchId DispatchId = iota
 
-       // Primitive (non-nullable) dispatch IDs - match Java's PRIMITIVE_* 
constants
-       PrimitiveBoolDispatchId
-       PrimitiveInt8DispatchId
-       PrimitiveInt16DispatchId
-       PrimitiveInt32DispatchId
-       PrimitiveVarint32DispatchId
-       PrimitiveInt64DispatchId
-       PrimitiveVarint64DispatchId
-       PrimitiveTaggedInt64DispatchId
-       PrimitiveFloat32DispatchId
-       PrimitiveFloat64DispatchId
-       PrimitiveUint8DispatchId
-       PrimitiveUint16DispatchId
-       PrimitiveUint32DispatchId
-       PrimitiveVarUint32DispatchId
-       PrimitiveUint64DispatchId
-       PrimitiveVarUint64DispatchId
-       PrimitiveTaggedUint64DispatchId
-       PrimitiveIntDispatchId  // Go-specific: native int
-       PrimitiveUintDispatchId // Go-specific: native uint
-
-       // Nullable dispatch IDs - match Java's non-PRIMITIVE_* constants
+       // ========== VARINT PRIMITIVES (contiguous for efficient jump table) 
==========
+       // These are used in the hot varint serialization loop
+       PrimitiveVarint32DispatchId     // 1 - int32 with varint encoding (most 
common)
+       PrimitiveVarint64DispatchId     // 2 - int64 with varint encoding
+       PrimitiveIntDispatchId          // 3 - Go-specific: native int
+       PrimitiveVarUint32DispatchId    // 4 - uint32 with varint encoding
+       PrimitiveVarUint64DispatchId    // 5 - uint64 with varint encoding
+       PrimitiveUintDispatchId         // 6 - Go-specific: native uint
+       PrimitiveTaggedInt64DispatchId  // 7 - int64 with tagged encoding
+       PrimitiveTaggedUint64DispatchId // 8 - uint64 with tagged encoding
+
+       // ========== FIXED-SIZE PRIMITIVES (contiguous for efficient jump 
table) ==========
+       // These are used in the hot fixed-size serialization loop
+       PrimitiveBoolDispatchId    // 9
+       PrimitiveInt8DispatchId    // 10
+       PrimitiveUint8DispatchId   // 11
+       PrimitiveInt16DispatchId   // 12
+       PrimitiveUint16DispatchId  // 13
+       PrimitiveInt32DispatchId   // 14 - int32 with fixed encoding
+       PrimitiveUint32DispatchId  // 15 - uint32 with fixed encoding
+       PrimitiveInt64DispatchId   // 16 - int64 with fixed encoding
+       PrimitiveUint64DispatchId  // 17 - uint64 with fixed encoding
+       PrimitiveFloat32DispatchId // 18
+       PrimitiveFloat64DispatchId // 19
+
+       // ========== NULLABLE DISPATCH IDs ==========
        NullableBoolDispatchId
        NullableInt8DispatchId
        NullableInt16DispatchId
@@ -321,8 +325,8 @@ const (
        NullableIntDispatchId  // Go-specific: *int
        NullableUintDispatchId // Go-specific: *uint
 
-       // Notnull pointer dispatch IDs - pointer types with nullable=false
-       // Write without null flag; on read, create default value if remote 
sends null
+       // ========== NOTNULL POINTER DISPATCH IDs ==========
+       // Pointer types with nullable=false - write without null flag
        NotnullBoolPtrDispatchId
        NotnullInt8PtrDispatchId
        NotnullInt16PtrDispatchId


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


Reply via email to