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]