diff --git a/go/fury/buffer.go b/go/fury/buffer.go index 80aeb6e203..d47dcf7939 100644 --- a/go/fury/buffer.go +++ b/go/fury/buffer.go @@ -89,21 +89,6 @@ func (b *ByteBuffer) WriteInt32(value int32) { binary.LittleEndian.PutUint32(b.data[b.writerIndex:], uint32(value)) b.writerIndex += 4 } -func (b *ByteBuffer) WriteVarUint32(value uint32) error { - // Ensure enough capacity (max 5 bytes for varint32) - b.grow(5) - - // Varint encoding - for value >= 0x80 { - b.data[b.writerIndex] = byte(value) | 0x80 - b.writerIndex++ - value >>= 7 - } - b.data[b.writerIndex] = byte(value) - b.writerIndex++ - - return nil -} func (b *ByteBuffer) WriteLength(value int) { b.grow(4) @@ -336,37 +321,250 @@ func (b *ByteBuffer) ReadVarInt32() int32 { return result } +type BufferObject interface { + TotalBytes() int + WriteTo(buf *ByteBuffer) + ToBuffer() *ByteBuffer +} + +// WriteVarint64 writes the zig-zag encoded varint +func (b *ByteBuffer) WriteVarint64(value int64) { + u := uint64((value << 1) ^ (value >> 63)) + b.WriteVarUint64(u) +} + +// WriteVarUint64 writes to unsigned varint (up to 9 bytes) +func (b *ByteBuffer) WriteVarUint64(value uint64) { + b.grow(9) + offset := b.writerIndex + data := b.data[offset : offset+9] + + i := 0 + for ; i < 8; i++ { + data[i] = byte(value & 0x7F) + value >>= 7 + if value == 0 { + i++ + break + } + data[i] |= 0x80 + } + if i == 8 { + data[8] = byte(value) + i = 9 + } + b.writerIndex += i +} + +// ReadVarint64 reads the varint encoded with zig-zag +func (b *ByteBuffer) ReadVarint64() int64 { + u := b.ReadVarUint64() + v := int64(u >> 1) + if u&1 != 0 { + v = ^v + } + return v +} + +// ReadVarUint64 reads unsigned varint +func (b *ByteBuffer) ReadVarUint64() uint64 { + if b.remaining() >= 9 { + return b.readVarUint64Fast() + } + return b.readVarUint64Slow() +} + +// Fast path (when the remaining bytes are sufficient) +func (b *ByteBuffer) readVarUint64Fast() uint64 { + data := b.data[b.readerIndex:] + var result uint64 + var readLength int + + b0 := data[0] + result = uint64(b0 & 0x7F) + if b0 < 0x80 { + readLength = 1 + } else { + b1 := data[1] + result |= uint64(b1&0x7F) << 7 + if b1 < 0x80 { + readLength = 2 + } else { + b2 := data[2] + result |= uint64(b2&0x7F) << 14 + if b2 < 0x80 { + readLength = 3 + } else { + b3 := data[3] + result |= uint64(b3&0x7F) << 21 + if b3 < 0x80 { + readLength = 4 + } else { + b4 := data[4] + result |= uint64(b4&0x7F) << 28 + if b4 < 0x80 { + readLength = 5 + } else { + b5 := data[5] + result |= uint64(b5&0x7F) << 35 + if b5 < 0x80 { + readLength = 6 + } else { + b6 := data[6] + result |= uint64(b6&0x7F) << 42 + if b6 < 0x80 { + readLength = 7 + } else { + b7 := data[7] + result |= uint64(b7&0x7F) << 49 + if b7 < 0x80 { + readLength = 8 + } else { + b8 := data[8] + result |= uint64(b8) << 56 + readLength = 9 + } + } + } + } + } + } + } + } + b.readerIndex += readLength + return result +} + +// Slow path (read byte by byte) +func (b *ByteBuffer) readVarUint64Slow() uint64 { + var result uint64 + var shift uint + for { + byteVal := b.ReadUint8() + result |= (uint64(byteVal) & 0x7F) << shift + if byteVal < 0x80 { + break + } + shift += 7 + if shift >= 64 { + panic("varuint64 overflow") + } + } + return result +} + +// Auxiliary function +func (b *ByteBuffer) remaining() int { + return len(b.data) - b.readerIndex +} + +func (b *ByteBuffer) ReadUint8() uint8 { + if b.readerIndex >= len(b.data) { + panic("buffer underflow") + } + v := b.data[b.readerIndex] + b.readerIndex++ + return v +} + +func (b *ByteBuffer) WriteVarint32(value int32) { + u := uint32((value << 1) ^ (value >> 31)) + b.WriteVarUint32(u) +} + +func (b *ByteBuffer) WriteVarUint32(value uint32) { + b.grow(5) + offset := b.writerIndex + data := b.data[offset : offset+5] + + i := 0 + for ; i < 4; i++ { + data[i] = byte(value & 0x7F) + value >>= 7 + if value == 0 { + i++ + break + } + data[i] |= 0x80 + } + if i == 4 { + data[4] = byte(value) + i = 5 + } + b.writerIndex += i +} + +func (b *ByteBuffer) ReadVarint32() int32 { + u := b.ReadVarUint32() + v := int32(u >> 1) + if u&1 != 0 { + v = ^v + } + return v +} + func (b *ByteBuffer) ReadVarUint32() uint32 { - readerIndex := b.readerIndex - byte_ := uint32(b.data[readerIndex]) - readerIndex++ - result := byte_ & 0x7F - if (byte_ & 0x80) != 0 { - byte_ = uint32(b.data[readerIndex]) - readerIndex++ - result |= (byte_ & 0x7F) << 7 - if (byte_ & 0x80) != 0 { - byte_ = uint32(b.data[readerIndex]) - readerIndex++ - result |= (byte_ & 0x7F) << 14 - if (byte_ & 0x80) != 0 { - byte_ = uint32(b.data[readerIndex]) - readerIndex++ - result |= (byte_ & 0x7F) << 21 - if (byte_ & 0x80) != 0 { - byte_ = uint32(b.data[readerIndex]) - readerIndex++ - result |= (byte_ & 0x7F) << 28 + if b.remaining() >= 5 { + return b.readVarUint32Fast() + } + return b.readVarUint32Slow() +} + +// Fast path reading (when the remaining bytes are sufficient) +func (b *ByteBuffer) readVarUint32Fast() uint32 { + data := b.data[b.readerIndex:] + var result uint32 + var readLength int + + b0 := data[0] + result = uint32(b0 & 0x7F) + if b0 < 0x80 { + readLength = 1 + } else { + b1 := data[1] + result |= uint32(b1&0x7F) << 7 + if b1 < 0x80 { + readLength = 2 + } else { + b2 := data[2] + result |= uint32(b2&0x7F) << 14 + if b2 < 0x80 { + readLength = 3 + } else { + b3 := data[3] + result |= uint32(b3&0x7F) << 21 + if b3 < 0x80 { + readLength = 4 + } else { + b4 := data[4] + result |= uint32(b4&0x7F) << 28 + readLength = 5 } } } } - b.readerIndex = readerIndex + b.readerIndex += readLength return result } -type BufferObject interface { - TotalBytes() int - WriteTo(buf *ByteBuffer) - ToBuffer() *ByteBuffer +// Slow path reading (processing byte by byte) +func (b *ByteBuffer) readVarUint32Slow() uint32 { + var result uint32 + var shift uint + for { + byteVal := b.ReadUint8() + result |= (uint32(byteVal) & 0x7F) << shift + if byteVal < 0x80 { + break + } + shift += 7 + if shift >= 28 { // 32位最多需要5字节(28位) + panic("varuint32 overflow") + } + } + return result +} + +func (b *ByteBuffer) PutUint8(writerIndex int, value uint8) { + b.data[writerIndex] = byte(value) } diff --git a/go/fury/fury.go b/go/fury/fury.go index 2d3ee5907c..46025e949f 100644 --- a/go/fury/fury.go +++ b/go/fury/fury.go @@ -160,7 +160,6 @@ func (f *Fury) Serialize(buf *ByteBuffer, v interface{}, callback BufferCallback } if f.language != XLANG { return fmt.Errorf("%d language is not supported", f.language) - buffer.WriteInt8(int8(GO)) } else { if err := buffer.WriteByte(GO); err != nil { return err @@ -179,10 +178,6 @@ func (f *Fury) Write(buffer *ByteBuffer, v interface{}) (err error) { buffer.WriteInt8(NullFlag) case bool: f.WriteBool(buffer, v) - case int32: - f.WriteInt32(buffer, v) - case int64: - f.WriteInt64(buffer, v) case float64: f.WriteFloat64(buffer, v) case float32: diff --git a/go/fury/fury_xlang_test.go b/go/fury/fury_xlang_test.go index 07b6b36d9b..63a4ea40b6 100644 --- a/go/fury/fury_xlang_test.go +++ b/go/fury/fury_xlang_test.go @@ -15,9 +15,6 @@ // specific language governing permissions and limitations // under the License. -//go:build skiptest -// +build skiptest - package fury_test import ( @@ -228,6 +225,8 @@ type ComplexObject2 struct { } func TestSerializeSimpleStruct(t *testing.T) { + // Temporarily disabled + t.Skip() fury_ := fury.NewFury(true) require.Nil(t, fury_.RegisterTagType("test.ComplexObject2", ComplexObject2{})) obj2 := &ComplexObject2{} @@ -237,6 +236,8 @@ func TestSerializeSimpleStruct(t *testing.T) { } func TestSerializeComplexStruct(t *testing.T) { + // Temporarily disabled + t.Skip() fury_ := fury.NewFury(true) require.Nil(t, fury_.RegisterTagType("test.ComplexObject1", ComplexObject1{})) require.Nil(t, fury_.RegisterTagType("test.ComplexObject2", ComplexObject2{})) @@ -286,6 +287,8 @@ func structRoundBack(t *testing.T, fury_ *fury.Fury, obj interface{}, testName s } func TestOutOfBandBuffer(t *testing.T) { + // Temporarily disabled + t.Skip() fury_ := fury.NewFury(true) var data [][]byte for i := 0; i < 10; i++ { diff --git a/go/fury/map.go b/go/fury/map.go index 689ef9f28d..152faa2827 100644 --- a/go/fury/map.go +++ b/go/fury/map.go @@ -17,7 +17,29 @@ package fury -import "reflect" +import ( + "errors" + "fmt" + "reflect" +) + +const ( + TRACKING_KEY_REF = 1 << 0 // 0b00000001 + KEY_HAS_NULL = 1 << 1 // 0b00000010 + KEY_DECL_TYPE = 1 << 2 // 0b00000100 + TRACKING_VALUE_REF = 1 << 3 // 0b00001000 + VALUE_HAS_NULL = 1 << 4 // 0b00010000 + VALUE_DECL_TYPE = 1 << 5 // 0b00100000 + MAX_CHUNK_SIZE = 255 +) + +const ( + KV_NULL = KEY_HAS_NULL | VALUE_HAS_NULL // 0b00010010 + NULL_KEY_VALUE_DECL_TYPE = KEY_HAS_NULL | VALUE_DECL_TYPE // 0b00100010 + NULL_KEY_VALUE_DECL_TYPE_TRACKING_REF = KEY_HAS_NULL | VALUE_DECL_TYPE | TRACKING_VALUE_REF // 0b00101010 + NULL_VALUE_KEY_DECL_TYPE = VALUE_HAS_NULL | KEY_DECL_TYPE // 0b00010100 + NULL_VALUE_KEY_DECL_TYPE_TRACKING_REF = VALUE_HAS_NULL | KEY_DECL_TYPE | TRACKING_KEY_REF // 0b00010101 +) type mapSerializer struct { } @@ -27,44 +49,341 @@ func (s mapSerializer) TypeId() TypeId { } func (s mapSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { + // Get map length and write it to buffer length := value.Len() - if err := f.writeLength(buf, length); err != nil { - return err + buf.WriteVarUint32(uint32(length)) + if length == 0 { + return nil } + + // Get resolvers from Fury instance + typeResolver := f.typeResolver + refResolver := f.refResolver + var keySerializer Serializer + var valueSerializer Serializer + + // Initialize map iterator and get first key-value pair iter := value.MapRange() - for iter.Next() { - if err := f.WriteReferencable(buf, iter.Key()); err != nil { - return err + if !iter.Next() { + return nil + } + key, val := iter.Key(), iter.Value() + hasNext := true + + for hasNext { + // Process null key/value pairs + for { + keyValid := isValid(key) + valValid := isValid(val) + + if keyValid && valValid { + break + } + + var header byte + switch { + case !keyValid && !valValid: + header = KV_NULL + case !keyValid: + header = KEY_HAS_NULL | TRACKING_VALUE_REF + if err := f.Write(buf, val); err != nil { + return err + } + case !valValid: + header = VALUE_HAS_NULL | TRACKING_KEY_REF + if err := f.Write(buf, key); err != nil { + return err + } + } + buf.WriteInt8(int8(header)) + + if iter.Next() { + key, val = iter.Key(), iter.Value() + } else { + hasNext = false + break + } + } + + if !hasNext { + break + } + + // Write chunk header placeholder (will be updated later) + chunkHeaderOffset := buf.WriterIndex() + buf.WriteInt16(-1) + + // Process type information + chunkHeader := byte(0) + if keySerializer == nil { + // Get key type info and write to buffer + keyTypeInfo, _ := getActualTypeInfo(key, typeResolver) + if err := typeResolver.writeTypeInfo(buf, keyTypeInfo); err != nil { + return err + } + keySerializer = keyTypeInfo.Serializer + } else { + chunkHeader |= KEY_DECL_TYPE // Key type already declared + } + + if valueSerializer == nil { + // Get value type info and write to buffer + valueTypeInfo, _ := getActualTypeInfo(val, typeResolver) + if err := typeResolver.writeTypeInfo(buf, valueTypeInfo); err != nil { + return err + } + valueSerializer = valueTypeInfo.Serializer + } else { + chunkHeader |= VALUE_DECL_TYPE // Value type already declared } - if err := f.WriteReferencable(buf, iter.Value()); err != nil { - return err + + // Set tracking flags if reference tracking is enabled + if f.referenceTracking { + chunkHeader |= TRACKING_KEY_REF + } + if f.referenceTracking { + chunkHeader |= TRACKING_VALUE_REF + } + + // Write chunk header + buf.PutUint8(chunkHeaderOffset, chunkHeader) + chunkSize := 0 + + // Serialize elements of same type in chunks + keyType := getActualType(key) + valueType := getActualType(val) + for chunkSize < MAX_CHUNK_SIZE { + if !isValid(key) || !isValid(val) || getActualType(key) != keyType || getActualType(val) != valueType { + break + } + + // Write key + key = UnwrapReflectValue(key) + if f.referenceTracking { + if written, err := refResolver.WriteRefOrNull(buf, key); err != nil { + return err + } else if !written { + if err := keySerializer.Write(f, buf, key); err != nil { + return err + } + } + } else { + if err := keySerializer.Write(f, buf, key); err != nil { + return err + } + } + + // Write value + val = UnwrapReflectValue(val) + if f.referenceTracking { + if written, err := refResolver.WriteRefOrNull(buf, val); err != nil { + return err + } else if !written { + if err := valueSerializer.Write(f, buf, val); err != nil { + return err + } + } + } else { + if err := valueSerializer.Write(f, buf, val); err != nil { + return err + } + } + + chunkSize++ + if iter.Next() { + key, val = iter.Key(), iter.Value() + } else { + hasNext = false + break + } } + + // Reset serializers for next chunk + keySerializer = nil + valueSerializer = nil + // Update chunk size in header + buf.PutUint8(chunkHeaderOffset+1, uint8(chunkSize)) } return nil } -func (s mapSerializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { +func (s mapSerializer) Read(f *Fury, buf *ByteBuffer, typ reflect.Type, value reflect.Value) error { + // Initialize map if nil if value.IsNil() { - value.Set(reflect.MakeMap(type_)) + value.Set(reflect.MakeMap(typ)) } + // Register reference for tracking f.refResolver.Reference(value) - keyType := type_.Key() - valueType := type_.Elem() - length := f.readLength(buf) - for i := 0; i < length; i++ { - mapKey := reflect.New(keyType).Elem() - if err := f.ReadReferencable(buf, mapKey); err != nil { - return err + + // Read map length from buffer + length := buf.ReadVarUint32() + + var keySerializer Serializer + var valueSerializer Serializer + typeResolver := f.typeResolver + refResolver := f.refResolver + + remaining := int(length) + for remaining > 0 { + header := buf.ReadUint8() + + // Handle special cases based on header flags + switch { + case header == (KEY_HAS_NULL | VALUE_HAS_NULL): + // Null key and null value case + value.SetMapIndex(reflect.Zero(typ.Key()), reflect.Zero(typ.Elem())) + remaining-- + continue + + case (header & (KEY_HAS_NULL | VALUE_DECL_TYPE)) == (KEY_HAS_NULL | VALUE_DECL_TYPE): + // Null key with declared value type case + trackValueRef := (header & TRACKING_VALUE_REF) != 0 + var key, val reflect.Value + + key = reflect.Zero(typ.Key()) + if trackValueRef { + // Handle reference tracking for value + if refID, err := refResolver.TryPreserveRefId(buf); err != nil { + return err + } else if refID >= 0 { + val = refResolver.GetReadObject(refID) + } else { + val = reflect.New(typ.Elem()).Elem() + if err := valueSerializer.Read(f, buf, val.Type(), val); err != nil { + return err + } + refResolver.SetReadObject(refID, val) + } + } else { + // Read value without reference tracking + val = reflect.New(typ.Elem()).Elem() + if err := valueSerializer.Read(f, buf, val.Type(), val); err != nil { + return err + } + } + value.SetMapIndex(key, val) + remaining-- + continue } - mapValue := reflect.New(valueType).Elem() - if err := f.ReadReferencable(buf, mapValue); err != nil { - return err + + // Chunk reading logic + chunkHeader := header + chunkSize := int(buf.ReadUint8()) + + // Read type information if not declared + if chunkHeader&KEY_DECL_TYPE == 0 { + keyTypeInfo, err := typeResolver.readTypeInfo(buf) + if err != nil { + return err + } + keySerializer = keyTypeInfo.Serializer + } + if chunkHeader&VALUE_DECL_TYPE == 0 { + valueTypeInfo, err := typeResolver.readTypeInfo(buf) + if err != nil { + return err + } + valueSerializer = valueTypeInfo.Serializer + } + + // Check reference tracking flags + trackKeyRef := chunkHeader&TRACKING_KEY_REF != 0 + trackValueRef := chunkHeader&TRACKING_VALUE_REF != 0 + + // Process each element in the chunk + for i := 0; i < chunkSize; i++ { + if remaining <= 0 { + return errors.New("invalid chunk size") + } + + // Read key with or without reference tracking + var key reflect.Value + var refID int32 + if trackKeyRef { + refID, _ = refResolver.TryPreserveRefId(buf) + + if int8(refID) < NotNullValueFlag { + key = refResolver.GetCurrentReadObject() + } else { + key, _ = actualVal(typ.Key()) + if err := keySerializer.Read(f, buf, key.Type(), key); err != nil { + return err + } + refResolver.SetReadObject(refID, key) + } + } else { + key, _ = actualVal(typ.Key()) + if err := keySerializer.Read(f, buf, key.Type(), key); err != nil { + return err + } + } + + // Read value with or without reference tracking + var val reflect.Value + if trackValueRef { + refID, _ = refResolver.TryPreserveRefId(buf) + + if int8(refID) < NotNullValueFlag { + val = refResolver.GetCurrentReadObject() + } else { + val, _ = actualVal(typ.Elem()) + if err := valueSerializer.Read(f, buf, val.Type(), val); err != nil { + return err + } + refResolver.SetReadObject(refID, val) + } + } else { + val, _ = actualVal(typ.Elem()) + if err := valueSerializer.Read(f, buf, val.Type(), val); err != nil { + return err + } + } + + // Store key-value pair in map + value.SetMapIndex(key, val) + remaining-- } - value.SetMapIndex(mapKey, mapValue) } return nil } +func getActualType(v reflect.Value) reflect.Type { + if v.Kind() == reflect.Interface && !v.IsNil() { + return v.Elem().Type() + } + return v.Type() +} + +func getActualTypeInfo(v reflect.Value, resolver *typeResolver) (TypeInfo, error) { + if v.Kind() == reflect.Interface && !v.IsNil() { + elem := v.Elem() + if !elem.IsValid() { + return TypeInfo{}, fmt.Errorf("invalid interface value") + } + return resolver.getTypeInfo(elem, true) + } + return resolver.getTypeInfo(v, true) +} + +func UnwrapReflectValue(v reflect.Value) reflect.Value { + for v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +func actualVal(t reflect.Type) (reflect.Value, error) { + if t.Kind() == reflect.Interface { + var container interface{} + return reflect.ValueOf(&container).Elem(), nil + } + return reflect.New(t).Elem(), nil +} + +func isValid(v reflect.Value) bool { + return v.IsValid() && !v.IsZero() +} + type mapConcreteKeyValueSerializer struct { type_ reflect.Type keySerializer Serializer diff --git a/go/fury/serializer.go b/go/fury/serializer.go index 7d94a979b8..97ff4ee6f0 100644 --- a/go/fury/serializer.go +++ b/go/fury/serializer.go @@ -106,12 +106,12 @@ func (s int32Serializer) TypeId() TypeId { } func (s int32Serializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { - buf.WriteInt32(int32(value.Int())) + buf.WriteVarint32(int32(value.Int())) return nil } func (s int32Serializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { - value.Set(reflect.ValueOf(buf.ReadInt32())) + value.Set(reflect.ValueOf(buf.ReadVarint32())) return nil } @@ -123,12 +123,12 @@ func (s int64Serializer) TypeId() TypeId { } func (s int64Serializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { - buf.WriteInt64(value.Int()) + buf.WriteVarint64(value.Int()) return nil } func (s int64Serializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { - value.Set(reflect.ValueOf(buf.ReadInt64())) + value.Set(reflect.ValueOf(buf.ReadVarint64())) return nil } @@ -195,14 +195,11 @@ func (s stringSerializer) TypeId() TypeId { } func (s stringSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { - // string bytes data reference has been handled in `referenceResolver`. - // We handle string reference instead of string data reference for cross-language compatibility. return writeString(buf, value.Interface().(string)) } func (s stringSerializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { - str := string(buf.ReadBinary(int(buf.ReadVarInt32()))) - value.Set(reflect.ValueOf(str)) + value.Set(reflect.ValueOf(readString(buf))) return nil } @@ -216,31 +213,21 @@ func (s ptrToStringSerializer) TypeId() TypeId { } func (s ptrToStringSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { - // string reference has been handled in `referenceResolver` by ptr type. - // We handle string reference instead of string data reference for cross-language compatibility. - return writeString(buf, value.Elem().Interface().(string)) + + if value.Kind() != reflect.Ptr || value.IsNil() { + return fmt.Errorf("expected non-nil string pointer, got %v", value.Type()) + } + str := value.Elem().Interface().(string) + return writeString(buf, str) } func (s ptrToStringSerializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { - str := string(readStringBytes(buf)) - value.Set(reflect.ValueOf(&str)) - return nil -} -func writeString(buf *ByteBuffer, value string) error { - strBytes := unsafeGetBytes(value) - if len(strBytes) >= MaxInt32 { - return fmt.Errorf("too long string: %d", len(strBytes)) - } - buf.WriteVarInt32(int32(len(strBytes))) - buf.WriteBinary(strBytes) + str := readString(buf) + value.Set(reflect.ValueOf(&str)) return nil } -func readString(buf *ByteBuffer) string { - return string(readStringBytes(buf)) -} - func readStringBytes(buf *ByteBuffer) []byte { return buf.ReadBinary(int(buf.ReadVarInt32())) } @@ -351,7 +338,7 @@ type dateSerializer struct { } func (s dateSerializer) TypeId() TypeId { - return DATE32 + return LOCAL_DATE } func (s dateSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { diff --git a/go/fury/set.go b/go/fury/set.go index e5f8fd3890..dc0bf2f7c4 100644 --- a/go/fury/set.go +++ b/go/fury/set.go @@ -33,35 +33,255 @@ type setSerializer struct { } func (s setSerializer) TypeId() TypeId { - return FURY_SET + return SET } func (s setSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { - mapData := value.Interface().(GenericSet) - if err := f.writeLength(buf, len(mapData)); err != nil { - return err + // Get all map keys (set elements) + keys := value.MapKeys() + length := len(keys) + + // Handle empty set case + if length == 0 { + buf.WriteVarUint32(0) // Write 0 length for empty set + return nil + } + + // Write collection header and get type information + collectFlag, elemTypeInfo := s.writeHeader(f, buf, keys) + + // Check if all elements are of same type + if (collectFlag & CollectionNotSameType) == 0 { + // Optimized path for same-type elements + return s.writeSameType(f, buf, keys, elemTypeInfo, collectFlag) + } + // Fallback path for mixed-type elements + return s.writeDifferentTypes(f, buf, keys) +} + +// writeHeader prepares and writes collection metadata including: +// - Collection size +// - Type consistency flags +// - Element type information (if homogeneous) +func (s setSerializer) writeHeader(f *Fury, buf *ByteBuffer, keys []reflect.Value) (byte, TypeInfo) { + // Initialize collection flags and type tracking variables + collectFlag := CollectionDefaultFlag + var elemTypeInfo TypeInfo + hasNull := false + hasDifferentType := false + + // Check elements to detect types + // Initialize element type information from first non-null element + if len(keys) > 0 { + firstElem := UnwrapReflectValue(keys[0]) + if isNull(firstElem) { + hasNull = true + } else { + // Get type info for first element to use as reference + elemTypeInfo, _ = f.typeResolver.getTypeInfo(firstElem, true) + } + } + + // Iterate through elements to check for nulls and type consistency + for _, key := range keys { + key = UnwrapReflectValue(key) + if isNull(key) { + hasNull = true + continue + } + + // Compare each element's type with the reference type + currentTypeInfo, _ := f.typeResolver.getTypeInfo(key, true) + if currentTypeInfo.TypeID != elemTypeInfo.TypeID { + hasDifferentType = true + } + } + + // Set collection flags based on findings + if hasNull { + collectFlag |= CollectionHasNull // Mark if collection contains null values } - for k := range mapData { - if err := f.WriteReferencable(buf, reflect.ValueOf(k)); err != nil { + if hasDifferentType { + collectFlag |= CollectionNotSameType // Mark if elements have different types + } + + // Enable reference tracking if configured + if f.referenceTracking { + collectFlag |= CollectionTrackingRef + } + + // Write metadata to buffer + buf.WriteVarUint32(uint32(len(keys))) // Collection size + buf.WriteInt8(int8(collectFlag)) // Collection flags + + // Write element type ID if all elements have same type + if !hasDifferentType { + buf.WriteVarInt32(elemTypeInfo.TypeID) + } + + return byte(collectFlag), elemTypeInfo +} + +// writeSameType efficiently serializes a collection where all elements share the same type +func (s setSerializer) writeSameType(f *Fury, buf *ByteBuffer, keys []reflect.Value, typeInfo TypeInfo, flag byte) error { + serializer := typeInfo.Serializer + trackRefs := (flag & CollectionTrackingRef) != 0 // Check if reference tracking is enabled + + for _, key := range keys { + key = UnwrapReflectValue(key) + if isNull(key) { + buf.WriteInt8(NullFlag) // Write null marker + continue + } + + if trackRefs { + // Handle reference tracking if enabled + refWritten, err := f.refResolver.WriteRefOrNull(buf, key) + if err != nil { + return err + } + if !refWritten { + // Write actual value if not a reference + if err := serializer.Write(f, buf, key); err != nil { + return err + } + } + } else { + // Directly write value without reference tracking + if err := serializer.Write(f, buf, key); err != nil { + return err + } + } + } + return nil +} + +// writeDifferentTypes handles serialization of collections with mixed element types +func (s setSerializer) writeDifferentTypes(f *Fury, buf *ByteBuffer, keys []reflect.Value) error { + for _, key := range keys { + key = UnwrapReflectValue(key) + if isNull(key) { + buf.WriteInt8(NullFlag) // Write null marker + continue + } + + // Get type info for each element (since types vary) + typeInfo, _ := f.typeResolver.getTypeInfo(key, true) + + // Handle reference tracking + refWritten, err := f.refResolver.WriteRefOrNull(buf, key) + if err != nil { return err } + + // Write type ID for each element + buf.WriteVarInt32(typeInfo.TypeID) + + if !refWritten { + // Write actual value if not a reference + if err := typeInfo.Serializer.Write(f, buf, key); err != nil { + return err + } + } } return nil } +// Read deserializes a set from the buffer into the provided reflect.Value func (s setSerializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { + // Read collection length from buffer + length := int(buf.ReadVarUint32()) + if length == 0 { + // Initialize empty set if length is 0 + value.Set(reflect.MakeMap(type_)) + return nil + } + + // Read collection flags that indicate special characteristics + collectFlag := buf.ReadInt8() + var elemTypeInfo TypeInfo + + // If all elements are same type, read the shared type info + if (collectFlag & CollectionNotSameType) == 0 { + typeID := buf.ReadVarInt32() + elemTypeInfo, _ = f.typeResolver.getTypeInfoById(int16(typeID)) + } + + // Initialize set if nil if value.IsNil() { - value.Set(reflect.ValueOf(GenericSet{})) + value.Set(reflect.MakeMap(type_)) } + // Register reference for tracking f.refResolver.Reference(value) - genericSet := value.Interface().(GenericSet) - length := f.readLength(buf) + + // Choose appropriate deserialization path based on type consistency + if (collectFlag & CollectionNotSameType) == 0 { + return s.readSameType(f, buf, value, elemTypeInfo, collectFlag, length) + } + return s.readDifferentTypes(f, buf, value, length) +} + +// readSameType handles deserialization of sets where all elements share the same type +func (s setSerializer) readSameType(f *Fury, buf *ByteBuffer, value reflect.Value, typeInfo TypeInfo, flag int8, length int) error { + // Determine if reference tracking is enabled + trackRefs := (flag & CollectionTrackingRef) != 0 + serializer := typeInfo.Serializer + + for i := 0; i < length; i++ { + var refID int32 + if trackRefs { + // Handle reference tracking if enabled + refID, _ = f.refResolver.TryPreserveRefId(buf) + if int8(refID) < NotNullValueFlag { + // Use existing reference if available + elem := f.refResolver.GetReadObject(refID) + value.SetMapIndex(reflect.ValueOf(elem), reflect.ValueOf(true)) + continue + } + } + + // Create new element and deserialize from buffer + elem := reflect.New(typeInfo.Type).Elem() + if err := serializer.Read(f, buf, elem.Type(), elem); err != nil { + return err + } + + // Register new reference if tracking + if trackRefs { + f.refResolver.SetReadObject(refID, elem) + } + // Add element to set + value.SetMapIndex(elem, reflect.ValueOf(true)) + } + return nil +} + +// readDifferentTypes handles deserialization of sets with mixed element types +func (s setSerializer) readDifferentTypes(f *Fury, buf *ByteBuffer, value reflect.Value, length int) error { for i := 0; i < length; i++ { - var mapKey interface{} - if err := f.ReadReferencable(buf, reflect.ValueOf(&mapKey).Elem()); err != nil { + // Handle reference tracking for each element + refID, _ := f.refResolver.TryPreserveRefId(buf) + // Read type ID for each element (since types vary) + typeID := buf.ReadVarInt32() + typeInfo, _ := f.typeResolver.getTypeInfoById(int16(typeID)) + + if int8(refID) < NotNullValueFlag { + // Use existing reference if available + elem := f.refResolver.GetReadObject(refID) + value.SetMapIndex(reflect.ValueOf(elem), reflect.ValueOf(true)) + continue + } + + // Create new element and deserialize from buffer + elem := reflect.New(typeInfo.Type).Elem() + if err := typeInfo.Serializer.Read(f, buf, elem.Type(), elem); err != nil { return err } - genericSet[mapKey] = true + + // Register new reference + f.refResolver.SetReadObject(refID, elem) + // Add element to set + value.SetMapIndex(elem, reflect.ValueOf(true)) } return nil } diff --git a/go/fury/slice.go b/go/fury/slice.go index 92cc1a2aff..17a8076480 100644 --- a/go/fury/slice.go +++ b/go/fury/slice.go @@ -22,42 +22,260 @@ import ( "reflect" ) -type sliceSerializer struct { -} +const ( + CollectionDefaultFlag = 0b0000 + CollectionTrackingRef = 0b0001 + CollectionHasNull = 0b0010 + CollectionNotDeclElementType = 0b0100 + CollectionNotSameType = 0b1000 +) + +type sliceSerializer struct{} func (s sliceSerializer) TypeId() TypeId { return LIST } +// Write serializes a slice value into the buffer func (s sliceSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { + // Get slice length and handle empty slice case length := value.Len() - if err := f.writeLength(buf, length); err != nil { - return err + if length == 0 { + buf.WriteVarUint32(0) // Write 0 for empty slice + return nil } - for i := 0; i < length; i++ { - if err := f.WriteReferencable(buf, value.Index(i)); err != nil { + + // Write collection header and get type information + collectFlag, elemTypeInfo := s.writeHeader(f, buf, value) + + // Choose serialization path based on type consistency + if (collectFlag & CollectionNotSameType) == 0 { + return s.writeSameType(f, buf, value, elemTypeInfo, collectFlag) // Optimized path for same-type elements + } + return s.writeDifferentTypes(f, buf, value) // Fallback path for mixed-type elements +} + +// writeHeader prepares and writes collection metadata including: +// - Collection size +// - Type consistency flags +// - Element type information (if homogeneous) +func (s sliceSerializer) writeHeader(f *Fury, buf *ByteBuffer, value reflect.Value) (byte, TypeInfo) { + collectFlag := CollectionDefaultFlag + var elemTypeInfo TypeInfo + hasNull := false + hasDifferentType := false + + // Get type information for the first element + elemTypeInfo, _ = f.typeResolver.getTypeInfo(value, true) + collectFlag |= CollectionNotDeclElementType + + // Iterate through elements to check for nulls and type consistency + for i := 0; i < value.Len(); i++ { + elem := value.Index(i).Elem() + if isNull(elem) { + hasNull = true + continue + } + + // Compare each element's type with the first element's type + currentTypeInfo, _ := f.typeResolver.getTypeInfo(elem, true) + if currentTypeInfo.TypeID != elemTypeInfo.TypeID { + hasDifferentType = true + } + } + + // Set collection flags based on findings + if hasNull { + collectFlag |= CollectionHasNull // Mark if collection contains null values + } + if hasDifferentType { + collectFlag |= CollectionNotSameType // Mark if elements have different types + } + + // Enable reference tracking if configured + if f.referenceTracking { + collectFlag |= CollectionTrackingRef + } + + // Write metadata to buffer + buf.WriteVarUint32(uint32(value.Len())) // Collection size + buf.WriteInt8(int8(collectFlag)) // Collection flags + + // Write element type ID if all elements have same type + if !hasDifferentType { + buf.WriteVarInt32(elemTypeInfo.TypeID) + } + + return byte(collectFlag), elemTypeInfo +} + +// writeSameType efficiently serializes a slice where all elements share the same type +func (s sliceSerializer) writeSameType(f *Fury, buf *ByteBuffer, value reflect.Value, typeInfo TypeInfo, flag byte) error { + serializer := typeInfo.Serializer + trackRefs := (flag & CollectionTrackingRef) != 0 // Check if reference tracking is enabled + + for i := 0; i < value.Len(); i++ { + elem := value.Index(i).Elem() + if isNull(elem) { + buf.WriteInt8(NullFlag) // Write null marker + continue + } + + if trackRefs { + // Handle reference tracking if enabled + refWritten, err := f.refResolver.WriteRefOrNull(buf, elem) + if err != nil { + return err + } + if !refWritten { + // Write actual value if not a reference + if err := serializer.Write(f, buf, elem); err != nil { + return err + } + } + } else { + // Directly write value without reference tracking + if err := serializer.Write(f, buf, elem); err != nil { + return err + } + } + } + return nil +} + +// writeDifferentTypes handles serialization of slices with mixed element types +func (s sliceSerializer) writeDifferentTypes(f *Fury, buf *ByteBuffer, value reflect.Value) error { + for i := 0; i < value.Len(); i++ { + elem := value.Index(i).Elem() + if isNull(elem) { + buf.WriteInt8(NullFlag) // Write null marker + continue + } + + // Handle reference tracking + refWritten, err := f.refResolver.WriteRefOrNull(buf, elem) + if err != nil { + return err + } + if refWritten { + continue // Skip if element was written as reference + } + + // Get and write type info for each element (since types vary) + typeInfo, _ := f.typeResolver.getTypeInfo(elem, true) + buf.WriteVarInt32(typeInfo.TypeID) + + // Write actual value + if err := typeInfo.Serializer.Write(f, buf, elem); err != nil { return err } } return nil } + +// Read deserializes a slice from the buffer into the provided reflect.Value func (s sliceSerializer) Read(f *Fury, buf *ByteBuffer, type_ reflect.Type, value reflect.Value) error { - length := f.readLength(buf) + // Read slice length from buffer + length := int(buf.ReadVarUint32()) + if length == 0 { + // Initialize empty slice if length is 0 + value.Set(reflect.MakeSlice(type_, 0, 0)) + return nil + } + + // Read collection flags that indicate special characteristics + collectFlag := buf.ReadInt8() + var elemTypeInfo TypeInfo + + // Read element type information if all elements are same type + if (collectFlag & CollectionNotSameType) == 0 { + typeID := buf.ReadVarInt32() + elemTypeInfo, _ = f.typeResolver.getTypeInfoById(int16(typeID)) + } + + // Initialize slice with proper capacity if value.Cap() < length { - value.Set(reflect.MakeSlice(value.Type(), length, length)) - } else if value.Len() < length { + value.Set(reflect.MakeSlice(type_, length, length)) + } else { value.Set(value.Slice(0, length)) } + // Register reference for tracking f.refResolver.Reference(value) - for i := 0; i < length; i++ { - elem := value.Index(i) - if err := f.ReadReferencable(buf, elem); err != nil { + + // Choose appropriate deserialization path based on type consistency + if (collectFlag & CollectionNotSameType) == 0 { + return s.readSameType(f, buf, value, elemTypeInfo, collectFlag) + } + return s.readDifferentTypes(f, buf, value) +} + +// readSameType handles deserialization of slices where all elements share the same type +func (s sliceSerializer) readSameType(f *Fury, buf *ByteBuffer, value reflect.Value, typeInfo TypeInfo, flag int8) error { + // Determine if reference tracking is enabled + trackRefs := (flag & CollectionTrackingRef) != 0 + serializer := typeInfo.Serializer + var refID int32 + + for i := 0; i < value.Len(); i++ { + if trackRefs { + // Handle reference tracking if enabled + refID, _ = f.refResolver.TryPreserveRefId(buf) + if int8(refID) < NotNullValueFlag { + // Use existing reference if available + value.Index(i).Set(f.refResolver.GetCurrentReadObject()) + continue + } + } + + // Create new element of the correct type and deserialize from buffer + elem := reflect.New(typeInfo.Type).Elem() + if err := serializer.Read(f, buf, elem.Type(), elem); err != nil { + return err + } + // Set element in slice and register reference + value.Index(i).Set(elem) + f.refResolver.SetReadObject(refID, elem) + } + return nil +} + +// readDifferentTypes handles deserialization of slices with mixed element types +func (s sliceSerializer) readDifferentTypes(f *Fury, buf *ByteBuffer, value reflect.Value) error { + for i := 0; i < value.Len(); i++ { + // Handle reference tracking for each element + refID, _ := f.refResolver.TryPreserveRefId(buf) + if int8(refID) < NotNullValueFlag { + // Use existing reference if available + value.Index(i).Set(f.refResolver.GetCurrentReadObject()) + continue + } + + // Read type ID for each element (since types vary) + typeID := buf.ReadVarInt32() + typeInfo, _ := f.typeResolver.getTypeInfoById(int16(typeID)) + + // Create new element and deserialize from buffer + elem := reflect.New(typeInfo.Type).Elem() + if err := typeInfo.Serializer.Read(f, buf, typeInfo.Type, elem); err != nil { return err } + // Set element in slice and register reference + f.refResolver.SetReadObject(refID, elem) + value.Index(i).Set(elem) } return nil } +// Helper function to check if a value is null/nil +func isNull(v reflect.Value) bool { + switch v.Kind() { + case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map, reflect.Func: + return v.IsNil() // Check if reference types are nil + default: + return false // Value types are never null + } +} + // sliceConcreteValueSerializer serialize a slice whose elem is not an interface or pointer to interface type sliceConcreteValueSerializer struct { type_ reflect.Type diff --git a/go/fury/string.go b/go/fury/string.go new file mode 100644 index 0000000000..46ff8da86a --- /dev/null +++ b/go/fury/string.go @@ -0,0 +1,153 @@ +// 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 fury + +import ( + "fmt" + "unicode/utf16" +) + +// Encoding type constants +const ( + encodingLatin1 = iota // Latin1/ISO-8859-1 encoding + encodingUTF16LE // UTF-16 Little Endian encoding + encodingUTF8 // UTF-8 encoding (default) +) + +// writeString implements string serialization with automatic encoding detection +func writeString(buf *ByteBuffer, value string) error { + // Check if string can be encoded as Latin1 + if isLatin1(value) { + return writeLatin1(buf, value) + } + + // Check if UTF-16LE encoding is more efficient + if utf16Bytes, ok := tryUTF16LE(value); ok { + return writeUTF16LE(buf, utf16Bytes) + } + + // Default to UTF-8 encoding + return writeUTF8(buf, value) +} + +// readString implements string deserialization with encoding parsing +func readString(buf *ByteBuffer) string { + header := buf.ReadVarUint64() + size := header >> 2 // Extract string length (in characters) + encoding := header & 0b11 // Extract encoding type + + switch encoding { + case encodingLatin1: + return readLatin1(buf, int(size)) + case encodingUTF16LE: + return readUTF16LE(buf, int(size)) + case encodingUTF8: + return readUTF8(buf, int(size)) + default: + panic(fmt.Sprintf("invalid string encoding: %d", encoding)) + } +} + +// Encoding detection helper functions +func isLatin1(s string) bool { + // Check if all runes fit within Latin1 range (0-255) + for _, r := range s { + if r > 0xFF { + return false + } + } + return true +} + +func tryUTF16LE(s string) ([]byte, bool) { + runes := []rune(s) + utf16Runes := utf16.Encode(runes) + + // Check for surrogate pairs (indicates complex Unicode) + hasSurrogate := false + for _, r := range utf16Runes { + if r >= 0xD800 && r <= 0xDFFF { + hasSurrogate = true + break + } + } + + if hasSurrogate { + return nil, false + } + + // Convert to Little Endian byte order + buf := make([]byte, 2*len(utf16Runes)) + for i, r := range utf16Runes { + buf[2*i] = byte(r) // Low byte + buf[2*i+1] = byte(r >> 8) // High byte + } + return buf, true +} + +// Specific encoding write methods +func writeLatin1(buf *ByteBuffer, s string) error { + length := len(s) + header := (uint64(length) << 2) | encodingLatin1 // Pack length and encoding + + buf.WriteVarUint64(header) + buf.WriteBinary(unsafeGetBytes(s)) // Directly use underlying bytes (Latin1 chars are compatible with UTF-8 in Go) + return nil +} + +func writeUTF16LE(buf *ByteBuffer, data []byte) error { + length := len(data) / 2 // Character count (2 bytes per char) + header := (uint64(length) << 2) | encodingUTF16LE + + buf.WriteVarUint64(header) + buf.WriteBinary(data) + return nil +} + +func writeUTF8(buf *ByteBuffer, s string) error { + data := unsafeGetBytes(s) + header := (uint64(len(data)) << 2) | encodingUTF8 + + buf.WriteVarUint64(header) + buf.WriteBinary(data) + return nil +} + +// Specific encoding read methods +func readLatin1(buf *ByteBuffer, size int) string { + data := buf.ReadBinary(size) + return string(data) // Go automatically handles Latin1 to UTF-8 conversion +} + +func readUTF16LE(buf *ByteBuffer, charCount int) string { + byteCount := charCount * 2 + data := buf.ReadBinary(byteCount) + + // Reconstruct UTF-16 code units + u16s := make([]uint16, charCount) + for i := 0; i < byteCount; i += 2 { + u16s[i/2] = uint16(data[i]) | uint16(data[i+1])<<8 + } + + return string(utf16.Decode(u16s)) +} + +func readUTF8(buf *ByteBuffer, size int) string { + data := buf.ReadBinary(size) + return string(data) // Direct UTF-8 conversion +} diff --git a/go/fury/struct.go b/go/fury/struct.go index 78d19293b8..bf06febc3c 100644 --- a/go/fury/struct.go +++ b/go/fury/struct.go @@ -33,7 +33,7 @@ type structSerializer struct { } func (s *structSerializer) TypeId() TypeId { - return -FURY_TYPE_TAG + return NAMED_STRUCT } func (s *structSerializer) Write(f *Fury, buf *ByteBuffer, value reflect.Value) error { diff --git a/go/fury/type.go b/go/fury/type.go index 95af639466..36cfbd9b78 100644 --- a/go/fury/type.go +++ b/go/fury/type.go @@ -269,14 +269,14 @@ type typeResolver struct { // Type tracking dynamicWrittenMetaStr []string - typeIDToClassInfo map[int32]TypeInfo + typeIDToTypeInfo map[int32]TypeInfo typeIDCounter int32 dynamicWriteStringID int32 // Class registries - classesInfo map[string]TypeInfo - nsTypeToClassInfo map[nsTypeKey]TypeInfo - namedTypeToClassInfo map[namedTypeKey]TypeInfo + typesInfo map[string]TypeInfo + nsTypeToTypeInfo map[nsTypeKey]TypeInfo + namedTypeToTypeInfo map[namedTypeKey]TypeInfo // Encoders/Decoders namespaceEncoder *meta.Encoder @@ -306,13 +306,13 @@ func newTypeResolver(fury *Fury) *typeResolver { hashToClassInfo: make(map[uint64]TypeInfo), dynamicWrittenMetaStr: make([]string, 0), - typeIDToClassInfo: make(map[int32]TypeInfo), + typeIDToTypeInfo: make(map[int32]TypeInfo), typeIDCounter: 300, dynamicWriteStringID: 0, - classesInfo: make(map[string]TypeInfo), - nsTypeToClassInfo: make(map[nsTypeKey]TypeInfo), - namedTypeToClassInfo: make(map[namedTypeKey]TypeInfo), + typesInfo: make(map[string]TypeInfo), + nsTypeToTypeInfo: make(map[nsTypeKey]TypeInfo), + namedTypeToTypeInfo: make(map[namedTypeKey]TypeInfo), namespaceEncoder: meta.NewEncoder('.', '_'), namespaceDecoder: meta.NewDecoder('.', '_'), @@ -453,7 +453,8 @@ func (r *typeResolver) getSerializerByTypeTag(typeTag string) (Serializer, error func (r *typeResolver) getTypeInfo(value reflect.Value, create bool) (TypeInfo, error) { // First check if type info exists in cache - if info, ok := r.classesInfo[value.Type().String()]; ok { + typeString := value.Type().String() + if info, ok := r.typesInfo[typeString]; ok { if info.Serializer == nil { // Lazy initialize serializer if not created yet serializer, err := r.createSerializer(value.Type()) @@ -497,6 +498,10 @@ func (r *typeResolver) getTypeInfo(value reflect.Value, create bool) (TypeInfo, default: fmt.Errorf("type %v must be registered explicitly", typ) } + // TThere are still some problems in order to adapt the struct + if value.Kind() == reflect.Struct { + typeID = NAMED_STRUCT + } // Register the type with full metadata return r.registerType( @@ -574,18 +579,18 @@ func (r *typeResolver) registerType( // Update resolver caches: tname := typ.String() - r.classesInfo[tname] = typeInfo // Cache by type string + r.typesInfo[tname] = typeInfo // Cache by type string if typeName != "" { // Cache by namespace/name pair - r.namedTypeToClassInfo[[2]string{namespace, typeName}] = typeInfo + r.namedTypeToTypeInfo[[2]string{namespace, typeName}] = typeInfo // Cache by hashed namespace/name bytes - r.nsTypeToClassInfo[nsTypeKey{nsBytes.Hashcode, typeBytes.Hashcode}] = typeInfo + r.nsTypeToTypeInfo[nsTypeKey{nsBytes.Hashcode, typeBytes.Hashcode}] = typeInfo } // Cache by type ID (for cross-language support) if r.language == XLANG || !IsNamespacedType(TypeId(typeID)) { - r.typeIDToClassInfo[typeID] = typeInfo + r.typeIDToTypeInfo[typeID] = typeInfo } return typeInfo, fmt.Errorf("registerType error") @@ -612,9 +617,7 @@ func (r *typeResolver) writeTypeInfo(buffer *ByteBuffer, typeInfo TypeInfo) erro internalTypeID := typeID & 0xFF // Write the type ID to buffer (variable-length encoding) - if err := buffer.WriteVarUint32(uint32(typeID)); err != nil { - return err - } + buffer.WriteVarUint32(uint32(typeID)) // For namespaced types, write additional metadata: if IsNamespacedType(TypeId(internalTypeID)) { @@ -871,7 +874,7 @@ func (r *typeResolver) readTypeInfo(buffer *ByteBuffer) (TypeInfo, error) { compositeKey := nsTypeKey{nsBytes.Hashcode, typeBytes.Hashcode} var typeInfo TypeInfo - if typeInfo, exists := r.nsTypeToClassInfo[compositeKey]; exists { + if typeInfo, exists := r.nsTypeToTypeInfo[compositeKey]; exists { return typeInfo, nil } @@ -887,8 +890,8 @@ func (r *typeResolver) readTypeInfo(buffer *ByteBuffer) (TypeInfo, error) { } nameKey := [2]string{ns, typeName} - if typeInfo, exists := r.namedTypeToClassInfo[nameKey]; exists { - r.nsTypeToClassInfo[compositeKey] = typeInfo + if typeInfo, exists := r.namedTypeToTypeInfo[nameKey]; exists { + r.nsTypeToTypeInfo[compositeKey] = typeInfo return typeInfo, nil } @@ -902,7 +905,7 @@ func (r *typeResolver) readTypeInfo(buffer *ByteBuffer) (TypeInfo, error) { // Handle simple type IDs (non-namespaced types) - if typeInfo, exists := r.typeIDToClassInfo[typeID]; exists { + if typeInfo, exists := r.typeIDToTypeInfo[typeID]; exists { return typeInfo, nil } @@ -926,6 +929,11 @@ func (r *typeResolver) getTypeById(id int16) (reflect.Type, error) { return type_, nil } +func (r *typeResolver) getTypeInfoById(id int16) (TypeInfo, error) { + typeInfo := r.typeIDToTypeInfo[int32(id)] + return typeInfo, nil +} + func (r *typeResolver) writeMetaString(buffer *ByteBuffer, str string) error { if id, ok := r.dynamicStringToId[str]; !ok { dynamicStringId := r.dynamicStringId