WIP: Update struct decode and encode #2
@ -1,141 +1,177 @@
|
||||
package structcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewDecode(r io.Reader, body any) error {
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
/*
|
||||
Este bloco de codigo server apenas para facilitar minha vida no mundo do go
|
||||
Definindo a estrutura de seguinte maneira, os dados estão fortemente ligado as structs go então qualquer merda aqui pode ferra com qualquer versão anterior, então isso não será recomendado para algumas coisa
|
||||
Os dados serão escritos em BigEndian, então tenha cuidado com os dados inseridos casos sejam inportantes
|
||||
|
||||
structType := reflect.TypeOf(body)
|
||||
if structType.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("must pass a pointer")
|
||||
}
|
||||
elem := structType.Elem()
|
||||
kind := elem.Kind()
|
||||
value := reflect.ValueOf(body)
|
||||
if kind == reflect.Struct {
|
||||
structurePointer := value.Elem()
|
||||
for i := range structurePointer.NumField() {
|
||||
field := elem.Field(i)
|
||||
fieldPointer := structurePointer.FieldByName(field.Name)
|
||||
kind := field.Type.Kind()
|
||||
switch kind {
|
||||
default:
|
||||
newValue := reflect.New(fieldPointer.Type()).Interface()
|
||||
if err := NewDecode(r, newValue); err != nil {
|
||||
fmt.Printf("unable to convert value to [%s][%s]\n", fieldPointer.Type().Kind(), field.Name)
|
||||
break
|
||||
}
|
||||
fieldPointer.Set(reflect.ValueOf(newValue).Elem())
|
||||
case reflect.Struct:
|
||||
nestedStruct := reflect.New(fieldPointer.Type()).Interface()
|
||||
if err := NewDecode(r, nestedStruct); err != nil {
|
||||
return err
|
||||
}
|
||||
fieldPointer.Set(reflect.ValueOf(nestedStruct).Elem())
|
||||
}
|
||||
}
|
||||
}
|
||||
time.Time -> time.Time.UnixMilli()
|
||||
[]bytes = Int (Size) + []bytes
|
||||
String -> Int (Size) + []bytes
|
||||
bool -> 0/1
|
||||
int, uint, int8, uint8 = int
|
||||
int32, uint32 = int32
|
||||
int64, uint64 = int64
|
||||
*/
|
||||
|
||||
valuePtr := value.Elem()
|
||||
switch kind {
|
||||
default:
|
||||
break
|
||||
case reflect.String:
|
||||
var size int64
|
||||
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
|
||||
return err
|
||||
}
|
||||
buff := make([]byte, size)
|
||||
if err := binary.Read(r, binary.BigEndian, buff); err != nil {
|
||||
return err
|
||||
}
|
||||
valuePtr.Set(reflect.ValueOf(string(buff)).Elem())
|
||||
case reflect.Slice:
|
||||
if data, ok := reflect.New(valuePtr.Type()).Interface().([]byte); ok {
|
||||
size := int64(len(data))
|
||||
if size == 0 {
|
||||
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := r.Read(data); err != nil {
|
||||
return err
|
||||
}
|
||||
valuePtr.Set(reflect.ValueOf(data).Elem())
|
||||
return nil
|
||||
}
|
||||
case
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
newValue := reflect.New(valuePtr.Type()).Interface()
|
||||
if err := binary.Read(r, binary.BigEndian, newValue); err != nil {
|
||||
return err
|
||||
}
|
||||
valuePtr.Set(reflect.ValueOf(newValue).Elem())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
typeofByte = reflect.TypeOf(([]byte{0})[0])
|
||||
)
|
||||
|
||||
func NewEncode(w io.Writer, body any) error {
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
structType := reflect.TypeOf(body)
|
||||
if structType.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("must pass a pointer")
|
||||
}
|
||||
elem := reflect.TypeOf(body).Elem()
|
||||
valuePtr := reflect.ValueOf(body).Elem()
|
||||
|
||||
elem := structType.Elem()
|
||||
kind := elem.Kind()
|
||||
value := reflect.ValueOf(body)
|
||||
if kind == reflect.Struct {
|
||||
structurePointer := value.Elem()
|
||||
for i := range structurePointer.NumField() {
|
||||
switch elem.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := range valuePtr.NumField() {
|
||||
field := elem.Field(i)
|
||||
fieldPointer := structurePointer.FieldByName(field.Name)
|
||||
kind := field.Type.Kind()
|
||||
switch kind {
|
||||
default:
|
||||
if err := NewEncode(w, fieldPointer.Interface()); err != nil {
|
||||
fmt.Printf("unable to convert value to [%s][%s]\n", fieldPointer.Type().Kind(), field.Name)
|
||||
fieldPointer := valuePtr.FieldByName(field.Name)
|
||||
var err error
|
||||
switch v := fieldPointer.Interface().(type) {
|
||||
case bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64:
|
||||
err = binary.Write(w, binary.BigEndian, v)
|
||||
case string:
|
||||
if err = binary.Write(w, binary.BigEndian, uint32(fieldPointer.Len())); err == nil {
|
||||
_, err = w.Write([]byte(v))
|
||||
}
|
||||
case reflect.Struct:
|
||||
if err := NewEncode(w, fieldPointer.Interface()); err != nil {
|
||||
case time.Time:
|
||||
err = binary.Write(w, binary.BigEndian, v.UnixMicro())
|
||||
case []byte:
|
||||
if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err == nil {
|
||||
_, err = w.Write(v)
|
||||
}
|
||||
default:
|
||||
if fieldPointer.IsNil() {
|
||||
if err = binary.Write(w, binary.BigEndian, uint8(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err = binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
|
||||
return err
|
||||
}
|
||||
err = NewEncode(w, v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
valuePtr := value.Elem()
|
||||
switch kind {
|
||||
default:
|
||||
break
|
||||
case reflect.String, reflect.Slice:
|
||||
content := valuePtr.Bytes()
|
||||
if err := binary.Write(w, binary.BigEndian, int64(len(content))); err != nil {
|
||||
return err
|
||||
} else if err := binary.Write(w, binary.BigEndian, content); err != nil {
|
||||
return err
|
||||
}
|
||||
case
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if err := binary.Write(w, binary.BigEndian, valuePtr.Interface()); err != nil {
|
||||
return err
|
||||
func NewDecode(r io.Reader, body any) error {
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
elem := reflect.TypeOf(body).Elem()
|
||||
valuePtr := reflect.ValueOf(body).Elem()
|
||||
fmt.Println(elem.String())
|
||||
|
||||
switch elem.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := range valuePtr.NumField() {
|
||||
field := elem.Field(i)
|
||||
fieldPointer := valuePtr.FieldByName(field.Name)
|
||||
fmt.Printf("Decode: %s: %s\n", field.Name, field.Type.String())
|
||||
|
||||
newValue := reflect.New(fieldPointer.Type()).Interface()
|
||||
var err error
|
||||
switch fieldPointer.Kind() {
|
||||
case reflect.Float32, reflect.Float64, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64:
|
||||
err = binary.Read(r, binary.BigEndian, newValue)
|
||||
case reflect.String:
|
||||
var size uint32
|
||||
if err = binary.Read(r, binary.BigEndian, &size); err == nil {
|
||||
buff := make([]byte, size)
|
||||
if _, err = r.Read(buff); err == nil {
|
||||
fieldPointer.SetString(string(buff))
|
||||
continue
|
||||
}
|
||||
}
|
||||
case reflect.Array: // [2]any
|
||||
if fieldPointer.Field(0).Type().String() == typeofByte.String() {
|
||||
if _, err = r.Read(newValue.([]byte)); err != nil {
|
||||
return err
|
||||
}
|
||||
fieldPointer.Set(reflect.Append(fieldPointer.Elem(), reflect.ValueOf(newValue)))
|
||||
continue
|
||||
}
|
||||
for i := range fieldPointer.Len() {
|
||||
newValue := reflect.New(fieldPointer.Field(i).Type()).Interface()
|
||||
if err = NewDecode(r, newValue); err != nil {
|
||||
return err
|
||||
}
|
||||
fieldPointer.Field(i).Set(reflect.ValueOf(newValue))
|
||||
}
|
||||
continue
|
||||
case reflect.Slice: // []any
|
||||
var size uint32
|
||||
if err = binary.Read(r, binary.BigEndian, &size); err != nil {
|
||||
return err
|
||||
} else if fieldPointer.Type().String() == "[]uint8" || fieldPointer.Type().String() == "*[]uint8" {
|
||||
buff := make([]byte, size)
|
||||
if _, err = r.Read(buff); err != nil {
|
||||
return err
|
||||
}
|
||||
fieldPointer.Set(reflect.ValueOf(buff))
|
||||
continue
|
||||
}
|
||||
default:
|
||||
switch fieldPointer.Type().String() {
|
||||
case "time.Time":
|
||||
var timestap int64
|
||||
if err = binary.Read(r, binary.BigEndian, ×tap); err == nil {
|
||||
fieldPointer.Set(reflect.ValueOf(time.UnixMicro(timestap)))
|
||||
continue
|
||||
}
|
||||
default:
|
||||
if strings.HasPrefix(fieldPointer.Type().String(), "*") {
|
||||
var ok uint8
|
||||
if err = binary.Read(r, binary.BigEndian, &ok); err == nil {
|
||||
if ok == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
err = NewDecode(r, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldPointer.Set(reflect.ValueOf(newValue).Elem())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Unmashall(b []byte, target any) error {
|
||||
return NewDecode(bytes.NewReader(b), target)
|
||||
}
|
||||
|
||||
func Marshall(target any) ([]byte, error) {
|
||||
buff := new(bytes.Buffer)
|
||||
err := NewEncode(buff, target)
|
||||
return buff.Bytes(), err
|
||||
}
|
||||
|
67
internal/structcode/structcode_test.go
Normal file
67
internal/structcode/structcode_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package structcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testBase struct {
|
||||
Text string
|
||||
Date time.Time
|
||||
Body []byte
|
||||
T1 float32
|
||||
T2 int8
|
||||
T3 bool
|
||||
T4 *testBase
|
||||
}
|
||||
|
||||
func TestDecodeEncode(t *testing.T) {
|
||||
var v1, v2 testBase
|
||||
v1.Text = "Golang is best programer language"
|
||||
v1.Body = make([]byte, 20)
|
||||
rand.Read(v1.Body)
|
||||
v1.Date = time.Now()
|
||||
v1.T1 = 0.2
|
||||
v1.T2 = 1
|
||||
v1.T4 = &testBase{
|
||||
Text: "Test t4",
|
||||
}
|
||||
|
||||
v1Body, err := Marshall(&v1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("Unsmarhall")
|
||||
t.Logf("Data: %q", base64.StdEncoding.EncodeToString(v1Body))
|
||||
|
||||
if err := Unmashall(v1Body, &v2); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
} else if v2.Text != v1.Text {
|
||||
t.Errorf("invalid unmarshall data, current %q, accept %q", v2.Text, v1.Text)
|
||||
return
|
||||
} else if !bytes.Equal(v2.Body, v1.Body) {
|
||||
t.Errorf("invalid unmarshall data, current %q, accept %q", hex.EncodeToString(v2.Body), hex.EncodeToString(v1.Body))
|
||||
return
|
||||
} else if v2.T1 != v1.T1 {
|
||||
t.Errorf("invalid unmarshall data, current %f, accept %f", v2.T1, v1.T1)
|
||||
return
|
||||
} else if v2.T2 != v1.T2 {
|
||||
t.Errorf("invalid unmarshall data, current %d, accept %d", v2.T2, v1.T2)
|
||||
return
|
||||
} else if v2.T3 != v1.T3 {
|
||||
t.Errorf("invalid unmarshall data, current %v, accept %v", v2.T3, v1.T3)
|
||||
return
|
||||
} else if v2.Date.UnixMilli() != v1.Date.UnixMilli() {
|
||||
t.Errorf("invalid unmarshall data, current %d, accept %d", v2.Date.UnixMilli(), v1.Date.UnixMilli())
|
||||
return
|
||||
} else if v2.T4.Text != v1.T4.Text {
|
||||
t.Errorf("invalid unmarshall data, current %s, accept %s", v2.T4.Text, v1.T4.Text)
|
||||
return
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user