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 e76f8784b feat(go): support [N]uint types array serializers (#3201)
e76f8784b is described below

commit e76f8784bb629611a707b31f6896f52e065dfa8e
Author: Ayush Kumar <[email protected]>
AuthorDate: Tue Jan 27 08:38:29 2026 +0530

    feat(go): support [N]uint types array serializers (#3201)
    
    Referenced issue - #3015
    
    ## Go Array Serialization Support
    I have implemented optimized array serialization for `uint16`, `uint32`,
    and `uint64` types in Go. These types now map to standard Fory type IDs
    (UINT16_ARRAY, UINT32_ARRAY, UINT64_ARRAY) instead of falling back to
    generic list serialization, ensuring better performance and
    cross-language compatibility.
    
    ### Changes
    - #### Serialization Logic -
    I updated `array_primitive.go` to include dedicated serializers:
    
    1. uint16ArraySerializer: Maps to UINT16_ARRAY
    2. uint32ArraySerializer: Maps to UINT32_ARRAY
    3. uint64ArraySerializer: Maps to UINT64_ARRAY
    
    These serializers support both fast-path (direct memory copy on
    little-endian systems) and standard path serialization.
    
    - #### Registered the typeIds and serializers to the `type_resolver.go`
    ```go
    case UINT8:
                    return &TypeInfo{Type: uint8Type, TypeID: typeID, 
Serializer: r.typeToSerializers[uint8Type], DispatchId: PrimitiveInt8DispatchId}
            case UINT16:
                    return &TypeInfo{Type: uint16Type, TypeID: typeID, 
Serializer: r.typeToSerializers[uint16Type], DispatchId: 
PrimitiveInt16DispatchId}
            case UINT32, VAR_UINT32:
                    return &TypeInfo{Type: uint32Type, TypeID: typeID, 
Serializer: r.typeToSerializers[uint32Type], DispatchId: 
PrimitiveInt32DispatchId}
            case UINT64, VAR_UINT64, TAGGED_UINT64:
                    return &TypeInfo{Type: uint64Type, TypeID: typeID, 
Serializer: r.typeToSerializers[uint64Type], DispatchId: 
PrimitiveInt64DispatchId}
    ```
    - #### Updated `slice.go` - `readSliceRefAndType` to return the Type ID
    (uint32) it reads from the buffer. Previously, it only returned a
    boolean flag.
    
    Before
    ```go
    func readSliceRefAndType(ctx *ReadContext, refMode RefMode, readType bool, 
value reflect.Value) (bool)
    ```
    
    After
    ```go
    func readSliceRefAndType(ctx *ReadContext, refMode RefMode, readType bool, 
value reflect.Value) (bool, uint32)
    ```
    
    - #### Updated `slice_primitive.go` - all primitive slice serializers
    (like `int64SliceSerializer`, `float32SliceSerializer`, etc.).
    Added a validation check: verifying that the returned typeId matches the
    expected wire type for that slice (e.g., []int64 now explicitly checks
    for INT64_ARRAY).
    If the ID doesn't match, it now returns a "slice type mismatch" error
    instead of silently proceeding with potentially corrupt data.
    
    ```go
            done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
            if done || ctx.HasError() {
                    return
            }
            if readType && typeId != uint32(BINARY) {
                    ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected BINARY (%d), got %d", BINARY, typeId))
                    return
            }
    ```
    
    - #### Updated `slice_dyn.go` - Updated the generic `sliceDynSerializer`
    to validate that the incoming wire type is LIST (since dynamic slices
    always use the LIST protocol).
    
    ```go
            done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
            if done || ctx.HasError() {
                    return
            }
            if readType && typeId != uint32(LIST) {
                    ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected LIST (%d), got %d", LIST, typeId))
                    return
            }
    ```
     ## Related issues
     Closes #3015
    
     ## 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
    N/A
    
    ---------
    
    Co-authored-by: Shawn Yang <[email protected]>
---
 go/fory/array_primitive.go      | 214 ++++++++++++++++++++++++++++++++++++++++
 go/fory/array_primitive_test.go | 132 +++++++++++++++++++++++++
 go/fory/slice.go                |  22 +++--
 go/fory/slice_dyn.go            |   6 +-
 go/fory/slice_primitive.go      |  78 ++++++++++++---
 go/fory/type_resolver.go        |  42 ++++++++
 6 files changed, 474 insertions(+), 20 deletions(-)

diff --git a/go/fory/array_primitive.go b/go/fory/array_primitive.go
index a6a3e469d..a50367287 100644
--- a/go/fory/array_primitive.go
+++ b/go/fory/array_primitive.go
@@ -580,3 +580,217 @@ func (s uint8ArraySerializer) Read(ctx *ReadContext, 
refMode RefMode, readType b
 func (s uint8ArraySerializer) ReadWithTypeInfo(ctx *ReadContext, refMode 
RefMode, typeInfo *TypeInfo, value reflect.Value) {
        s.Read(ctx, refMode, false, false, value)
 }
+
+// ============================================================================
+// uint16ArraySerializer - optimized [N]uint16 serialization
+// ============================================================================
+
+type uint16ArraySerializer struct {
+       arrayType reflect.Type
+}
+
+func (s uint16ArraySerializer) WriteData(ctx *WriteContext, value 
reflect.Value) {
+       buf := ctx.Buffer()
+       length := value.Len()
+       size := length * 2
+       buf.WriteLength(size)
+       if length > 0 {
+               if value.CanAddr() && isLittleEndian {
+                       // Fast path: direct memory copy - little-endian only
+                       ptr := value.Addr().UnsafePointer()
+                       buf.WriteBinary(unsafe.Slice((*byte)(ptr), size))
+               } else {
+                       // Slow path for non-addressable arrays or big-endian
+                       for i := 0; i < length; i++ {
+                               // Cast to int16 to match INT16_ARRAY wire 
format (bits are preserved)
+                               buf.WriteInt16(int16(value.Index(i).Uint()))
+                       }
+               }
+       }
+}
+
+func (s uint16ArraySerializer) Write(ctx *WriteContext, refMode RefMode, 
writeType bool, hasGenerics bool, value reflect.Value) {
+       writeArrayRefAndType(ctx, refMode, writeType, value, UINT16_ARRAY)
+       if ctx.HasError() {
+               return
+       }
+       s.WriteData(ctx, value)
+}
+
+func (s uint16ArraySerializer) ReadData(ctx *ReadContext, value reflect.Value) 
{
+       buf := ctx.Buffer()
+       err := ctx.Err()
+       size := buf.ReadLength(err)
+       length := size / 2
+       if ctx.HasError() {
+               return
+       }
+       if length != value.Type().Len() {
+               ctx.SetError(DeserializationErrorf("array length %d does not 
match type %v", length, value.Type()))
+               return
+       }
+       if length > 0 {
+               if isLittleEndian {
+                       ptr := value.Addr().UnsafePointer()
+                       raw := buf.ReadBinary(size, err)
+                       copy(unsafe.Slice((*byte)(ptr), size), raw)
+               } else {
+                       for i := 0; i < length; i++ {
+                               
value.Index(i).SetUint(uint64(uint16(buf.ReadInt16(err))))
+                       }
+               }
+       }
+}
+
+func (s uint16ArraySerializer) Read(ctx *ReadContext, refMode RefMode, 
readType bool, hasGenerics bool, value reflect.Value) {
+       done := readArrayRefAndType(ctx, refMode, readType, value)
+       if done || ctx.HasError() {
+               return
+       }
+       s.ReadData(ctx, value)
+}
+
+func (s uint16ArraySerializer) ReadWithTypeInfo(ctx *ReadContext, refMode 
RefMode, typeInfo *TypeInfo, value reflect.Value) {
+       s.Read(ctx, refMode, false, false, value)
+}
+
+// ============================================================================
+// uint32ArraySerializer - optimized [N]uint32 serialization
+// ============================================================================
+
+type uint32ArraySerializer struct {
+       arrayType reflect.Type
+}
+
+func (s uint32ArraySerializer) WriteData(ctx *WriteContext, value 
reflect.Value) {
+       buf := ctx.Buffer()
+       length := value.Len()
+       size := length * 4
+       buf.WriteLength(size)
+       if length > 0 {
+               if value.CanAddr() && isLittleEndian {
+                       ptr := value.Addr().UnsafePointer()
+                       buf.WriteBinary(unsafe.Slice((*byte)(ptr), size))
+               } else {
+                       for i := 0; i < length; i++ {
+                               // Cast to int32 (bits preserved)
+                               buf.WriteInt32(int32(value.Index(i).Uint()))
+                       }
+               }
+       }
+}
+
+func (s uint32ArraySerializer) Write(ctx *WriteContext, refMode RefMode, 
writeType bool, hasGenerics bool, value reflect.Value) {
+       writeArrayRefAndType(ctx, refMode, writeType, value, UINT32_ARRAY)
+       if ctx.HasError() {
+               return
+       }
+       s.WriteData(ctx, value)
+}
+
+func (s uint32ArraySerializer) ReadData(ctx *ReadContext, value reflect.Value) 
{
+       buf := ctx.Buffer()
+       err := ctx.Err()
+       size := buf.ReadLength(err)
+       length := size / 4
+       if ctx.HasError() {
+               return
+       }
+       if length != value.Type().Len() {
+               ctx.SetError(DeserializationErrorf("array length %d does not 
match type %v", length, value.Type()))
+               return
+       }
+       if length > 0 {
+               if isLittleEndian {
+                       ptr := value.Addr().UnsafePointer()
+                       raw := buf.ReadBinary(size, err)
+                       copy(unsafe.Slice((*byte)(ptr), size), raw)
+               } else {
+                       for i := 0; i < length; i++ {
+                               
value.Index(i).SetUint(uint64(uint32(buf.ReadInt32(err))))
+                       }
+               }
+       }
+}
+
+func (s uint32ArraySerializer) Read(ctx *ReadContext, refMode RefMode, 
readType bool, hasGenerics bool, value reflect.Value) {
+       done := readArrayRefAndType(ctx, refMode, readType, value)
+       if done || ctx.HasError() {
+               return
+       }
+       s.ReadData(ctx, value)
+}
+
+func (s uint32ArraySerializer) ReadWithTypeInfo(ctx *ReadContext, refMode 
RefMode, typeInfo *TypeInfo, value reflect.Value) {
+       s.Read(ctx, refMode, false, false, value)
+}
+
+// ============================================================================
+// uint64ArraySerializer - optimized [N]uint64 serialization
+// ============================================================================
+
+type uint64ArraySerializer struct {
+       arrayType reflect.Type
+}
+
+func (s uint64ArraySerializer) WriteData(ctx *WriteContext, value 
reflect.Value) {
+       buf := ctx.Buffer()
+       length := value.Len()
+       size := length * 8
+       buf.WriteLength(size)
+       if length > 0 {
+               if value.CanAddr() && isLittleEndian {
+                       ptr := value.Addr().UnsafePointer()
+                       buf.WriteBinary(unsafe.Slice((*byte)(ptr), size))
+               } else {
+                       for i := 0; i < length; i++ {
+                               buf.WriteInt64(int64(value.Index(i).Uint()))
+                       }
+               }
+       }
+}
+
+func (s uint64ArraySerializer) Write(ctx *WriteContext, refMode RefMode, 
writeType bool, hasGenerics bool, value reflect.Value) {
+       writeArrayRefAndType(ctx, refMode, writeType, value, UINT64_ARRAY)
+       if ctx.HasError() {
+               return
+       }
+       s.WriteData(ctx, value)
+}
+
+func (s uint64ArraySerializer) ReadData(ctx *ReadContext, value reflect.Value) 
{
+       buf := ctx.Buffer()
+       err := ctx.Err()
+       size := buf.ReadLength(err)
+       length := size / 8
+       if ctx.HasError() {
+               return
+       }
+       if length != value.Type().Len() {
+               ctx.SetError(DeserializationErrorf("array length %d does not 
match type %v", length, value.Type()))
+               return
+       }
+       if length > 0 {
+               if isLittleEndian {
+                       ptr := value.Addr().UnsafePointer()
+                       raw := buf.ReadBinary(size, err)
+                       copy(unsafe.Slice((*byte)(ptr), size), raw)
+               } else {
+                       for i := 0; i < length; i++ {
+                               
value.Index(i).SetUint(uint64(buf.ReadInt64(err)))
+                       }
+               }
+       }
+}
+
+func (s uint64ArraySerializer) Read(ctx *ReadContext, refMode RefMode, 
readType bool, hasGenerics bool, value reflect.Value) {
+       done := readArrayRefAndType(ctx, refMode, readType, value)
+       if done || ctx.HasError() {
+               return
+       }
+       s.ReadData(ctx, value)
+}
+
+func (s uint64ArraySerializer) ReadWithTypeInfo(ctx *ReadContext, refMode 
RefMode, typeInfo *TypeInfo, value reflect.Value) {
+       s.Read(ctx, refMode, false, false, value)
+}
diff --git a/go/fory/array_primitive_test.go b/go/fory/array_primitive_test.go
new file mode 100644
index 000000000..a55b6ede8
--- /dev/null
+++ b/go/fory/array_primitive_test.go
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package fory
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestPrimitiveArraySerializer(t *testing.T) {
+       f := NewFory()
+
+       // Test uint16 array
+       t.Run("uint16_array", func(t *testing.T) {
+               arr := [3]uint16{10, 20, 30}
+               data, err := f.Serialize(arr)
+               assert.NoError(t, err)
+
+               var result [3]uint16
+               err = f.Deserialize(data, &result)
+               assert.NoError(t, err)
+               assert.Equal(t, arr, result)
+       })
+
+       // Test uint32 array
+       t.Run("uint32_array", func(t *testing.T) {
+               arr := [3]uint32{100, 200, 300}
+               data, err := f.Serialize(arr)
+               assert.NoError(t, err)
+
+               var result [3]uint32
+               err = f.Deserialize(data, &result)
+               assert.NoError(t, err)
+               assert.Equal(t, arr, result)
+       })
+
+       // Test uint64 array
+       t.Run("uint64_array", func(t *testing.T) {
+               arr := [3]uint64{1000, 2000, 3000}
+               data, err := f.Serialize(arr)
+               assert.NoError(t, err)
+
+               var result [3]uint64
+               err = f.Deserialize(data, &result)
+               assert.NoError(t, err)
+               assert.Equal(t, arr, result)
+       })
+
+       t.Run("uint array", func(t *testing.T) {
+               arr := [3]uint{1, 100, 200}
+               bytes, err := f.Serialize(arr)
+               assert.NoError(t, err)
+
+               var result [3]uint
+               err = f.Unmarshal(bytes, &result)
+               require.NoError(t, err)
+               require.Equal(t, arr, result)
+       })
+}
+
+func TestArraySliceInteroperability(t *testing.T) {
+       f := NewFory()
+
+       t.Run("array_to_slice", func(t *testing.T) {
+               // Serialize Array [3]int32
+               arr := [3]int32{1, 2, 3}
+               data, err := f.Serialize(arr)
+               assert.NoError(t, err)
+
+               // Deserialize into Slice []int32
+               var slice []int32
+               err = f.Deserialize(data, &slice)
+               assert.NoError(t, err)
+               assert.Equal(t, []int32{1, 2, 3}, slice)
+       })
+
+       t.Run("slice_to_array", func(t *testing.T) {
+               // Serialize Slice []int32
+               slice := []int32{4, 5, 6}
+               data, err := f.Serialize(slice)
+               assert.NoError(t, err)
+
+               // Deserialize into Array [3]int32
+               var arr [3]int32
+               err = f.Deserialize(data, &arr)
+               assert.NoError(t, err)
+               assert.Equal(t, [3]int32{4, 5, 6}, arr)
+       })
+
+       t.Run("array_to_slice_mismatch_type", func(t *testing.T) {
+               // Serialize Array [3]int32
+               arr := [3]int32{1, 2, 3}
+               data, err := f.Serialize(arr)
+               assert.NoError(t, err)
+
+               var slice []int64 // different type
+               err = f.Deserialize(data, &slice)
+               // Strict checking means this should error immediately upon 
reading wrong TypeID
+               assert.Error(t, err)
+       })
+
+       t.Run("slice_to_array_size_mismatch", func(t *testing.T) {
+               // Serialize Slice []int32 with len 2
+               slice := []int32{1, 2}
+               data, err := f.Serialize(slice)
+               assert.NoError(t, err)
+
+               // Deserialize into Array [3]int32 - should fail size check
+               var arr [3]int32
+               err = f.Deserialize(data, &arr)
+               // Serialized as list with len 2. Array expects 3.
+               assert.Error(t, err)
+               assert.Contains(t, err.Error(), "array length")
+       })
+}
diff --git a/go/fory/slice.go b/go/fory/slice.go
index 8c0dec2ba..efaa75f46 100644
--- a/go/fory/slice.go
+++ b/go/fory/slice.go
@@ -62,8 +62,9 @@ func writeSliceRefAndType(ctx *WriteContext, refMode RefMode, 
writeType bool, va
 }
 
 // readSliceRefAndType handles reference and type reading for slice 
serializers.
-// Returns true if a reference was resolved (value already set), false if data 
should be read.
-func readSliceRefAndType(ctx *ReadContext, refMode RefMode, readType bool, 
value reflect.Value) bool {
+// Returns (true, 0) if a reference was resolved (value already set).
+// Returns (false, typeId) if data should be written and typeId was read (if 
readType=true).
+func readSliceRefAndType(ctx *ReadContext, refMode RefMode, readType bool, 
value reflect.Value) (bool, uint32) {
        buf := ctx.Buffer()
        ctxErr := ctx.Err()
        switch refMode {
@@ -71,25 +72,26 @@ func readSliceRefAndType(ctx *ReadContext, refMode RefMode, 
readType bool, value
                refID, refErr := ctx.RefResolver().TryPreserveRefId(buf)
                if refErr != nil {
                        ctx.SetError(FromError(refErr))
-                       return true
+                       return true, 0
                }
                if refID < int32(NotNullValueFlag) {
                        obj := ctx.RefResolver().GetReadObject(refID)
                        if obj.IsValid() {
                                value.Set(obj)
                        }
-                       return true
+                       return true, 0
                }
        case RefModeNullOnly:
                flag := buf.ReadInt8(ctxErr)
                if flag == NullFlag {
-                       return true
+                       return true, 0
                }
        }
+       var typeId uint32
        if readType {
-               buf.ReadVaruint32Small7(ctxErr)
+               typeId = buf.ReadVaruint32Small7(ctxErr)
        }
-       return false
+       return false, typeId
 }
 
 // Helper function to check if a value is null/nil
@@ -242,10 +244,14 @@ func (s *sliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType bo
 }
 
 func (s *sliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(LIST) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected LIST (%d), got %d", LIST, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
diff --git a/go/fory/slice_dyn.go b/go/fory/slice_dyn.go
index 3ac95ebe7..a4f36be4b 100644
--- a/go/fory/slice_dyn.go
+++ b/go/fory/slice_dyn.go
@@ -247,10 +247,14 @@ func (s sliceDynSerializer) writeDifferentTypes(ctx 
*WriteContext, buf *ByteBuff
 }
 
 func (s sliceDynSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(LIST) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected LIST (%d), got %d", LIST, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
diff --git a/go/fory/slice_primitive.go b/go/fory/slice_primitive.go
index 655cb3149..3397eaf7a 100644
--- a/go/fory/slice_primitive.go
+++ b/go/fory/slice_primitive.go
@@ -53,10 +53,14 @@ func (s byteSliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType
 }
 
 func (s byteSliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(BINARY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected BINARY (%d), got %d", BINARY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -114,10 +118,14 @@ func (s boolSliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType
 }
 
 func (s boolSliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(BOOL_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected BOOL_ARRAY (%d), got %d", BOOL_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -148,10 +156,14 @@ func (s int8SliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType
 }
 
 func (s int8SliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(INT8_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected INT8_ARRAY (%d), got %d", INT8_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -182,10 +194,14 @@ func (s int16SliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeTyp
 }
 
 func (s int16SliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(INT16_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected INT16_ARRAY (%d), got %d", INT16_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -216,10 +232,14 @@ func (s int32SliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeTyp
 }
 
 func (s int32SliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(INT32_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected INT32_ARRAY (%d), got %d", INT32_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -250,10 +270,14 @@ func (s int64SliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeTyp
 }
 
 func (s int64SliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(INT64_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected INT64_ARRAY (%d), got %d", INT64_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -284,10 +308,14 @@ func (s float32SliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeT
 }
 
 func (s float32SliceSerializer) Read(ctx *ReadContext, refMode RefMode, 
readType bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(FLOAT32_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected FLOAT32_ARRAY (%d), got %d", FLOAT32_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -318,10 +346,14 @@ func (s float64SliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeT
 }
 
 func (s float64SliceSerializer) Read(ctx *ReadContext, refMode RefMode, 
readType bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(FLOAT64_ARRAY) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected FLOAT64_ARRAY (%d), got %d", FLOAT64_ARRAY, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
@@ -356,10 +388,20 @@ func (s intSliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType
 }
 
 func (s intSliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType {
+               var expected TypeId = INT32_ARRAY
+               if strconv.IntSize == 64 {
+                       expected = INT64_ARRAY
+               }
+               if typeId != uint32(expected) {
+                       ctx.SetError(DeserializationErrorf("slice type 
mismatch: expected %d, got %d", expected, typeId))
+                       return
+               }
+       }
        s.ReadData(ctx, value)
 }
 
@@ -396,10 +438,20 @@ func (s uintSliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType
 }
 
 func (s uintSliceSerializer) Read(ctx *ReadContext, refMode RefMode, readType 
bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType {
+               var expected TypeId = INT32_ARRAY
+               if strconv.IntSize == 64 {
+                       expected = INT64_ARRAY
+               }
+               if typeId != uint32(expected) {
+                       ctx.SetError(DeserializationErrorf("slice type 
mismatch: expected %d, got %d", expected, typeId))
+                       return
+               }
+       }
        s.ReadData(ctx, value)
 }
 
@@ -455,10 +507,14 @@ func (s stringSliceSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeTy
 }
 
 func (s stringSliceSerializer) Read(ctx *ReadContext, refMode RefMode, 
readType bool, hasGenerics bool, value reflect.Value) {
-       done := readSliceRefAndType(ctx, refMode, readType, value)
+       done, typeId := readSliceRefAndType(ctx, refMode, readType, value)
        if done || ctx.HasError() {
                return
        }
+       if readType && typeId != uint32(LIST) {
+               ctx.SetError(DeserializationErrorf("slice type mismatch: 
expected LIST (%d), got %d", LIST, typeId))
+               return
+       }
        s.ReadData(ctx, value)
 }
 
diff --git a/go/fory/type_resolver.go b/go/fory/type_resolver.go
index ab7e86cc8..9b0cf4243 100644
--- a/go/fory/type_resolver.go
+++ b/go/fory/type_resolver.go
@@ -1040,6 +1040,9 @@ func (r *TypeResolver) getTypeInfo(value reflect.Value, 
create bool) (*TypeInfo,
        case r.isXlang && !r.requireRegistration:
                // Auto-assign IDs
                typeID = 0
+       case type_.Kind() == reflect.Array || type_.Kind() == reflect.Slice || 
type_.Kind() == reflect.Map:
+               // Allow anonymous collection types to use dynamic type ID 0
+               typeID = 0
        default:
                panic(fmt.Errorf("type %v must be registered explicitly", 
type_))
        }
@@ -1101,6 +1104,23 @@ func (r *TypeResolver) getTypeInfo(value reflect.Value, 
create bool) (*TypeInfo,
                                arrayTypeID = INT32_ARRAY
                                serializer = int32ArraySerializer{arrayType: 
type_}
                        }
+               case reflect.Uint16:
+                       arrayTypeID = UINT16_ARRAY
+                       serializer = uint16ArraySerializer{arrayType: type_}
+               case reflect.Uint32:
+                       arrayTypeID = UINT32_ARRAY
+                       serializer = uint32ArraySerializer{arrayType: type_}
+               case reflect.Uint64:
+                       arrayTypeID = UINT64_ARRAY
+                       serializer = uint64ArraySerializer{arrayType: type_}
+               case reflect.Uint:
+                       if intSize == 8 {
+                               arrayTypeID = UINT64_ARRAY
+                               serializer = uint64ArraySerializer{arrayType: 
type_}
+                       } else {
+                               arrayTypeID = UINT32_ARRAY
+                               serializer = uint32ArraySerializer{arrayType: 
type_}
+                       }
                default:
                        // Generic array - use LIST type ID
                        arrayTypeID = LIST
@@ -1530,7 +1550,21 @@ func (r *TypeResolver) createSerializer(type_ 
reflect.Type, mapInStruct bool) (s
                                return int64ArraySerializer{arrayType: type_}, 
nil
                        }
                        return int32ArraySerializer{arrayType: type_}, nil
+               case reflect.Uint16:
+                       return uint16ArraySerializer{arrayType: type_}, nil
+               case reflect.Uint32:
+                       return uint32ArraySerializer{arrayType: type_}, nil
+               case reflect.Uint64:
+                       return uint64ArraySerializer{arrayType: type_}, nil
+               case reflect.Uint:
+                       // Platform-dependent uint type - use uint32 or uint64 
array serializer
+                       // Wire format uses INT32_ARRAY or INT64_ARRAY 
respectively
+                       if reflect.TypeOf(uint(0)).Size() == 8 {
+                               return uint64ArraySerializer{arrayType: type_}, 
nil
+                       }
+                       return uint32ArraySerializer{arrayType: type_}, nil
                }
+
                if isDynamicType(elem) {
                        return arraySerializer{}, nil
                } else {
@@ -2114,6 +2148,14 @@ func (r *TypeResolver) readTypeInfoWithTypeID(buffer 
*ByteBuffer, typeID uint32,
                return &TypeInfo{Type: int32Type, TypeID: typeID, Serializer: 
r.typeToSerializers[int32Type], DispatchId: PrimitiveInt32DispatchId}
        case INT64, VARINT64, TAGGED_INT64:
                return &TypeInfo{Type: int64Type, TypeID: typeID, Serializer: 
r.typeToSerializers[int64Type], DispatchId: PrimitiveInt64DispatchId}
+       case UINT8:
+               return &TypeInfo{Type: uint8Type, TypeID: typeID, Serializer: 
r.typeToSerializers[uint8Type], DispatchId: PrimitiveInt8DispatchId}
+       case UINT16:
+               return &TypeInfo{Type: uint16Type, TypeID: typeID, Serializer: 
r.typeToSerializers[uint16Type], DispatchId: PrimitiveInt16DispatchId}
+       case UINT32, VAR_UINT32:
+               return &TypeInfo{Type: uint32Type, TypeID: typeID, Serializer: 
r.typeToSerializers[uint32Type], DispatchId: PrimitiveInt32DispatchId}
+       case UINT64, VAR_UINT64, TAGGED_UINT64:
+               return &TypeInfo{Type: uint64Type, TypeID: typeID, Serializer: 
r.typeToSerializers[uint64Type], DispatchId: PrimitiveInt64DispatchId}
        case FLOAT32:
                return &TypeInfo{Type: float32Type, TypeID: typeID, Serializer: 
r.typeToSerializers[float32Type], DispatchId: PrimitiveFloat32DispatchId}
        case FLOAT64:


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

Reply via email to