Skip to content

Commit

Permalink
add StructType & ListType support for decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
andot committed Feb 7, 2024
1 parent c74b566 commit acee437
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 12 deletions.
26 changes: 24 additions & 2 deletions io/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
| |
| io/decoder.go |
| |
| LastModified: Dec 13, 2023 |
| LastModified: Feb 7, 2024 |
| Author: Ma Bingyao <[email protected]> |
| |
\*________________________________________________________*/
Expand Down Expand Up @@ -63,6 +63,24 @@ const (
MapTypeSIMap
)

type StructType int8

const (
// StructTypeStructPointer represents the default type is *T.
StructTypeStructPointer StructType = iota
// StructTypeStructObject represents the default type is T.
StructTypeStructObject
)

type ListType int8

const (
// ListTypeInterfaceSlice represents the default type is []interface{}.
ListTypeInterfaceSlice ListType = iota
// ListTypeSlice represents the default type is []T.
ListTypeSlice
)

const defaultBufferSize = 256

// Decoder is a io.Reader like object, with hprose specific read functions.
Expand All @@ -79,6 +97,8 @@ type Decoder struct {
LongType
RealType
MapType
StructType
ListType
}

// NewDecoder creates an Decoder instance from byte array.
Expand Down Expand Up @@ -342,9 +362,11 @@ func (dec *Decoder) ResetBuffer() *Decoder {
dec.head = 0
dec.tail = 0
dec.Error = nil
dec.MapType = MapTypeIIMap
dec.RealType = RealTypeFloat64
dec.LongType = LongTypeInt
dec.MapType = MapTypeIIMap
dec.StructType = StructTypeStructPointer
dec.ListType = ListTypeInterfaceSlice
return dec
}

Expand Down
271 changes: 271 additions & 0 deletions io/interface_decoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/*--------------------------------------------------------*\
| |
| hprose |
| |
| Official WebSite: https://hprose.com |
| |
| io/interface_decoder_test.go |
| |
| LastModified: Feb 7, 2024 |
| Author: Ma Bingyao <[email protected]> |
| |
\*________________________________________________________*/

package io_test

import (
"strings"
"testing"

. "github.com/hprose/hprose-golang/v3/io"
"github.com/stretchr/testify/assert"
)

func TestDecodeIntSliceToInterface(t *testing.T) {
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode([]int{0, 1, 2, 3})

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{0, 1, 2, 3}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []int{0, 1, 2, 3}, v)
}

func TestDecodeInt64SliceToInterface(t *testing.T) {
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode([]int64{0, 1, 2, 3})

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{0, 1, 2, 3}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []int{0, 1, 2, 3}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.LongType = LongTypeInt64
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []int64{0, 1, 2, 3}, v)
}

func TestDecodeFloat64SliceToInterface(t *testing.T) {
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode([]float64{0, 1, 2, 3})

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{float64(0), float64(1), float64(2), float64(3)}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []float64{0, 1, 2, 3}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.RealType = RealTypeFloat32
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []float32{0, 1, 2, 3}, v)
}

func TestDecodeStringSliceToInterface(t *testing.T) {
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode([]string{"", "1", "2", "3"})

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{"", "1", "2", "3"}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []string{"", "1", "2", "3"}, v)
}

func TestDecodeStructSliceToInterface(t *testing.T) {
type TestStruct struct {
A int
B bool
C string
D float32
}
Register((*TestStruct)(nil))
data := []TestStruct{
{1, true, "1", 1},
{2, true, "2", 2},
{3, true, "3", 3},
}
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode(data)

expectedData1 := []interface{}{
&TestStruct{1, true, "1", 1},
&TestStruct{2, true, "2", 2},
&TestStruct{3, true, "3", 3},
}
dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, expectedData1, v)

expectedData2 := []interface{}{
TestStruct{1, true, "1", 1},
TestStruct{2, true, "2", 2},
TestStruct{3, true, "3", 3},
}

dec = NewDecoder(([]byte)(sb.String()))
dec.StructType = StructTypeStructObject
dec.Decode(&v)
assert.Equal(t, expectedData2, v)

expectedData3 := []*TestStruct{
{1, true, "1", 1},
{2, true, "2", 2},
{3, true, "3", 3},
}
dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, expectedData3, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.StructType = StructTypeStructObject
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, data, v)

}

func TestDecodeBytesSliceToInterface(t *testing.T) {
data := [][]byte{
{1, 2, 3},
{4, 5, 6},
nil,
{7, 8, 9},
}
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode(data)
dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{
[]byte{1, 2, 3},
[]byte{4, 5, 6},
nil,
[]byte{7, 8, 9},
}, v)
dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, data, v)
}

func TestDecodeIntSliceSliceToInterface(t *testing.T) {
data := [][]int{
{1, 2, 3},
{4, 5, 6},
nil,
{7, 8, 9},
}
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode(data)

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{
[]interface{}{1, 2, 3},
[]interface{}{4, 5, 6},
[]interface{}(nil),
[]interface{}{7, 8, 9},
}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, data, v)
}

func TestDecodeMapSliceToInterface(t *testing.T) {
data := []map[string]interface{}{
{"1": "1", "2": "2", "3": "3"},
{"4": "4", "5": "5", "6": "6"},
nil,
{"7": "7", "8": "8", "9": "9"},
}
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode(data)

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{
map[interface{}]interface{}{"1": "1", "2": "2", "3": "3"},
map[interface{}]interface{}{"4": "4", "5": "5", "6": "6"},
nil,
map[interface{}]interface{}{"7": "7", "8": "8", "9": "9"},
}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.MapType = MapTypeSIMap
dec.Decode(&v)
assert.Equal(t, []interface{}{
map[string]interface{}{"1": "1", "2": "2", "3": "3"},
map[string]interface{}{"4": "4", "5": "5", "6": "6"},
nil,
map[string]interface{}{"7": "7", "8": "8", "9": "9"},
}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []map[interface{}]interface{}{
{"1": "1", "2": "2", "3": "3"},
{"4": "4", "5": "5", "6": "6"},
nil,
{"7": "7", "8": "8", "9": "9"},
}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.MapType = MapTypeSIMap
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, data, v)
}

func TestDecodeInterfaceSliceToInterface(t *testing.T) {
sb := new(strings.Builder)
enc := NewEncoder(sb)
enc.Encode([]interface{}{1, 2.5, "3"})

dec := NewDecoder(([]byte)(sb.String()))
var v interface{}
dec.Decode(&v)
assert.Equal(t, []interface{}{1, 2.5, "3"}, v)

dec = NewDecoder(([]byte)(sb.String()))
dec.ListType = ListTypeSlice
dec.Decode(&v)
assert.Equal(t, []interface{}{1, 2.5, "3"}, v)

}
42 changes: 40 additions & 2 deletions io/interface_deocder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
| |
| io/interface_decoder.go |
| |
| LastModified: Feb 20, 2022 |
| LastModified: Feb 7, 2024 |
| Author: Ma Bingyao <[email protected]> |
| |
\*________________________________________________________*/
Expand All @@ -17,6 +17,8 @@ import (
"fmt"
"math"
"math/big"
"reflect"
"unsafe"

"github.com/modern-go/reflect2"
)
Expand Down Expand Up @@ -78,7 +80,43 @@ func (dec *Decoder) decodeDoubleAsInterface(p *interface{}) {
func (dec *Decoder) decodeListAsInterface(tag byte, p *interface{}) {
var result []interface{}
ifsdec.Decode(dec, &result, tag)
*p = result
n := len(result)
if n == 0 || dec.ListType == ListTypeInterfaceSlice {
*p = result
return
}
var t reflect.Type
for i := 0; i < n; i++ {
rt := reflect.TypeOf(result[i])
if isNil(result[i]) || rt.Kind() == reflect.Invalid || rt.Kind() == reflect.Interface {
continue
}
if t == nil {
t = rt
}
if rt != t {
*p = result
return
}
}
if t == nil {
*p = result
return
}
st := reflect2.Type2(reflect.SliceOf(t)).(*reflect2.UnsafeSliceType)
s := st.UnsafeMakeSlice(n, n)
for i := 0; i < n; i++ {
if isNil(result[i]) {
continue
}
p := reflect2.PtrOf(result[i])
if t.Kind() == reflect.Ptr || t.Kind() == reflect.Map {
st.UnsafeSetIndex(s, i, (unsafe.Pointer)(&p))
} else {
st.UnsafeSetIndex(s, i, p)
}
}
*p = st.UnsafeIndirect(s)
}

func (dec *Decoder) decodeMapAsInterface(tag byte, p *interface{}) {
Expand Down
Loading

0 comments on commit acee437

Please sign in to comment.