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 b63c3e989 feat(go): align cross-language type serialization for
primitive arrays (#2610)
b63c3e989 is described below
commit b63c3e9897bc2851c5835b01729c7f3c5604c7b6
Author: PAN <[email protected]>
AuthorDate: Sun Sep 14 19:49:53 2025 +0800
feat(go): align cross-language type serialization for primitive arrays
(#2610)
<!--
**Thanks for contributing to Apache Fory™.**
**If this is your first time opening a PR on fory, you can refer to
[CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).**
Contribution Checklist
- The **Apache Fory™** community has requirements on the naming of pr
titles. You can also find instructions in
[CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).
- Apache Fory™ has a strong focus on performance. If the PR you submit
will have an impact on performance, please benchmark it first and
provide the benchmark result here.
-->
## Why?
In the Fory Go implementation, slices of basic types (such as `[]int32`,
`[]bool`, etc.) were not correctly mapped to the corresponding array
types during cross-language serialization. Instead, they were treated as
generic LIST types, leading to inconsistencies with other languages
(Python, Java, etc.).
<!-- Describe the purpose of this PR. -->
## What does this PR do?
Specialized array serializers were added in `slice.go`:
- `boolArraySerializer` (TypeId: 30) - Handles `[]bool`
- `int8ArraySerializer` (TypeId: 31) - Handles `[]int8`
- `int16ArraySerializer` (TypeId: 32) - Handles `[]int16`
- `int32ArraySerializer` (TypeId: 33) - Handles `[]int32`
- `int64ArraySerializer` (TypeId: 34) - Handles `[]int64`
- `float32ArraySerializer` (TypeId: 36) - Handles `[]float32`
- `float64ArraySerializer` (TypeId: 37) - Handles `[]float64`
the correct TypeId mapping was ensured:
| Go Type | TypeId | Cross-Language Type |
|---------|--------|---------------------|
| []bool | 30 | bool_array |
| []int8 | 31 | int8_array |
| []int16 | 32 | int16_array |
| []int32 | 33 | int32_array |
| []int64 | 34 | int64_array |
| []float32 | 36 | float32_array |
| []float64 | 37 | float64_array |
| []int, []uint, []interface{} | 21 | list (maintains generality) |
- Platform-dependent types (`[]int`, `[]uint`) continue to use LIST
(TypeId: 21) to ensure cross-platform compatibility
- Generic types (`[]interface{}`) use LIST to maintain flexibility
- Only basic fixed-size types use specialized array serializers
<!-- Describe the details of this PR. -->
## Related issues
relate #2430
<!--
Is there any related issue? If this PR closes them you say say
fix/closes:
- #xxxx0
- #xxxx1
- Fixes #xxxx2
-->
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
Delete section if not applicable.
-->
---
go/fory/slice.go | 318 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
go/fory/type.go | 53 ++++++++--
2 files changed, 364 insertions(+), 7 deletions(-)
diff --git a/go/fory/slice.go b/go/fory/slice.go
index ccd9d4b11..6a171cdc4 100644
--- a/go/fory/slice.go
+++ b/go/fory/slice.go
@@ -439,11 +439,327 @@ func (o *ByteSliceBufferObject) ToBuffer() *ByteBuffer {
return NewByteBuffer(o.data)
}
+type boolArraySerializer struct {
+}
+
+func (s boolArraySerializer) TypeId() TypeId {
+ return BOOL_ARRAY
+}
+
+func (s boolArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s boolArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]bool)
+ size := len(v)
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteBool(elem)
+ }
+ return nil
+}
+
+func (s boolArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength()
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetBool(buf.ReadBool())
+ }
+ value.Set(r)
+ return nil
+}
+
+type int8ArraySerializer struct {
+}
+
+func (s int8ArraySerializer) TypeId() TypeId {
+ return INT8_ARRAY
+}
+
+func (s int8ArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s int8ArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]int8)
+ size := len(v)
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteByte_(byte(elem))
+ }
+ return nil
+}
+
+func (s int8ArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength()
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetInt(int64(int8(buf.ReadByte_())))
+ }
+ value.Set(r)
+ return nil
+}
+
+type int16ArraySerializer struct {
+}
+
+func (s int16ArraySerializer) TypeId() TypeId {
+ return INT16_ARRAY
+}
+
+func (s int16ArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s int16ArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]int16)
+ size := len(v) * 2
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteInt16(elem)
+ }
+ return nil
+}
+
+func (s int16ArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength() / 2
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetInt(int64(buf.ReadInt16()))
+ }
+ value.Set(r)
+ return nil
+}
+
+type int32ArraySerializer struct {
+}
+
+func (s int32ArraySerializer) TypeId() TypeId {
+ return INT32_ARRAY
+}
+
+func (s int32ArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s int32ArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]int32)
+ size := len(v) * 4
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteInt32(elem)
+ }
+ return nil
+}
+
+func (s int32ArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength() / 4
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetInt(int64(buf.ReadInt32()))
+ }
+ value.Set(r)
+ return nil
+}
+
+type int64ArraySerializer struct {
+}
+
+func (s int64ArraySerializer) TypeId() TypeId {
+ return INT64_ARRAY
+}
+
+func (s int64ArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s int64ArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]int64)
+ size := len(v) * 8
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteInt64(elem)
+ }
+ return nil
+}
+
+func (s int64ArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength() / 8
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetInt(buf.ReadInt64())
+ }
+ value.Set(r)
+ return nil
+}
+
+type float32ArraySerializer struct {
+}
+
+func (s float32ArraySerializer) TypeId() TypeId {
+ return FLOAT32_ARRAY
+}
+
+func (s float32ArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s float32ArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]float32)
+ size := len(v) * 4
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteFloat32(elem)
+ }
+ return nil
+}
+
+func (s float32ArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength() / 4
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetFloat(float64(buf.ReadFloat32()))
+ }
+ value.Set(r)
+ return nil
+}
+
+type float64ArraySerializer struct {
+}
+
+func (s float64ArraySerializer) TypeId() TypeId {
+ return FLOAT64_ARRAY
+}
+
+func (s float64ArraySerializer) NeedWriteRef() bool {
+ return true
+}
+
+func (s float64ArraySerializer) Write(f *Fory, buf *ByteBuffer, value
reflect.Value) error {
+ v := value.Interface().([]float64)
+ size := len(v) * 8
+ if size >= MaxInt32 {
+ return fmt.Errorf("too long slice: %d", len(v))
+ }
+ buf.WriteLength(size)
+ for _, elem := range v {
+ buf.WriteFloat64(elem)
+ }
+ return nil
+}
+
+func (s float64ArraySerializer) Read(f *Fory, buf *ByteBuffer, type_
reflect.Type, value reflect.Value) error {
+ length := buf.ReadLength() / 8
+ var r reflect.Value
+ switch type_.Kind() {
+ case reflect.Slice:
+ r = reflect.MakeSlice(type_, length, length)
+ case reflect.Array:
+ if length != type_.Len() {
+ return fmt.Errorf("length %d does not match array type
%v", length, type_)
+ }
+ r = reflect.New(type_).Elem()
+ default:
+ return fmt.Errorf("unsupported kind %v, want slice/array",
type_.Kind())
+ }
+ for i := 0; i < length; i++ {
+ r.Index(i).SetFloat(buf.ReadFloat64())
+ }
+ value.Set(r)
+ return nil
+}
+
+// Legacy slice serializers - kept for backward compatibility but not used for
xlang
type boolSliceSerializer struct {
}
func (s boolSliceSerializer) TypeId() TypeId {
- return BOOL_ARRAY
+ return BOOL_ARRAY // Use legacy type ID to avoid conflicts
}
func (s boolSliceSerializer) NeedWriteRef() bool {
diff --git a/go/fory/type.go b/go/fory/type.go
index 8ce94417e..7817a4a33 100644
--- a/go/fory/type.go
+++ b/go/fory/type.go
@@ -203,6 +203,7 @@ var (
stringSliceType = reflect.TypeOf((*[]string)(nil)).Elem()
byteSliceType = reflect.TypeOf((*[]byte)(nil)).Elem()
boolSliceType = reflect.TypeOf((*[]bool)(nil)).Elem()
+ int8SliceType = reflect.TypeOf((*[]int8)(nil)).Elem()
int16SliceType = reflect.TypeOf((*[]int16)(nil)).Elem()
int32SliceType = reflect.TypeOf((*[]int32)(nil)).Elem()
int64SliceType = reflect.TypeOf((*[]int64)(nil)).Elem()
@@ -421,12 +422,13 @@ func (r *typeResolver) initialize() {
{stringPtrType, ptrToStringSerializer{}},
{stringSliceType, stringSliceSerializer{}},
{byteSliceType, byteSliceSerializer{}},
- {boolSliceType, boolSliceSerializer{}},
- {int16SliceType, int16SliceSerializer{}},
- {int32SliceType, int32SliceSerializer{}},
- {int64SliceType, int64SliceSerializer{}},
- {float32SliceType, float32SliceSerializer{}},
- {float64SliceType, float64SliceSerializer{}},
+ // Map basic type slices to proper array types for xlang
compatibility
+ {boolSliceType, boolArraySerializer{}},
+ {int16SliceType, int16ArraySerializer{}},
+ {int32SliceType, int32ArraySerializer{}},
+ {int64SliceType, int64ArraySerializer{}},
+ {float32SliceType, float32ArraySerializer{}},
+ {float64SliceType, float64ArraySerializer{}},
{interfaceSliceType, sliceSerializer{}},
{interfaceMapType, mapSerializer{}},
{boolType, boolSerializer{}},
@@ -821,6 +823,45 @@ func (r *typeResolver) createSerializer(type_
reflect.Type, mapInStruct bool) (s
return &ptrToValueSerializer{valueSerializer}, nil
case reflect.Slice:
elem := type_.Elem()
+ // Handle special slice types for xlang compatibility
+ if r.language == XLANG {
+ // Basic type slices should use array types for
efficiency
+ switch elem.Kind() {
+ case reflect.Bool:
+ if type_ == boolSliceType {
+ return boolArraySerializer{}, nil
+ }
+ case reflect.Int8:
+ if type_ == int8SliceType {
+ return int8ArraySerializer{}, nil
+ }
+ case reflect.Int16:
+ if type_ == int16SliceType {
+ return int16ArraySerializer{}, nil
+ }
+ case reflect.Int32:
+ if type_ == int32SliceType {
+ return int32ArraySerializer{}, nil
+ }
+ case reflect.Int64:
+ if type_ == int64SliceType {
+ return int64ArraySerializer{}, nil
+ }
+ case reflect.Float32:
+ if type_ == float32SliceType {
+ return float32ArraySerializer{}, nil
+ }
+ case reflect.Float64:
+ if type_ == float64SliceType {
+ return float64ArraySerializer{}, nil
+ }
+ case reflect.Int, reflect.Uint:
+ // Platform-dependent types should use LIST for
cross-platform compatibility
+ // We treat them as dynamic types to force LIST
serialization
+ return sliceSerializer{}, nil
+ }
+ }
+ // For dynamic types or non-xlang mode, use generic slice
serializer
if isDynamicType(elem) {
return sliceSerializer{}, nil
} else {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]