WIP: Update struct decode and encode #2

Draft
Sirherobrine23 wants to merge 13 commits from struct-serealize into main
6 changed files with 255 additions and 6 deletions
Showing only changes of commit cf290429a6 - Show all commits

View File

@ -1,141 +1,177 @@
package structcode package structcode
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"time"
) )
func NewDecode(r io.Reader, body any) error { /*
if body == nil { Este bloco de codigo server apenas para facilitar minha vida no mundo do go
return nil 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) time.Time -> time.Time.UnixMilli()
if structType.Kind() != reflect.Ptr { []bytes = Int (Size) + []bytes
return fmt.Errorf("must pass a pointer") String -> Int (Size) + []bytes
} bool -> 0/1
elem := structType.Elem() int, uint, int8, uint8 = int
kind := elem.Kind() int32, uint32 = int32
value := reflect.ValueOf(body) int64, uint64 = int64
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())
}
}
}
valuePtr := value.Elem() var (
switch kind { typeofByte = reflect.TypeOf(([]byte{0})[0])
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
}
func NewEncode(w io.Writer, body any) error { func NewEncode(w io.Writer, body any) error {
if body == nil { if body == nil {
return nil return nil
} }
structType := reflect.TypeOf(body) elem := reflect.TypeOf(body).Elem()
if structType.Kind() != reflect.Ptr { valuePtr := reflect.ValueOf(body).Elem()
return fmt.Errorf("must pass a pointer")
}
elem := structType.Elem() switch elem.Kind() {
kind := elem.Kind() case reflect.Struct:
value := reflect.ValueOf(body) for i := range valuePtr.NumField() {
if kind == reflect.Struct {
structurePointer := value.Elem()
for i := range structurePointer.NumField() {
field := elem.Field(i) field := elem.Field(i)
fieldPointer := structurePointer.FieldByName(field.Name) fieldPointer := valuePtr.FieldByName(field.Name)
kind := field.Type.Kind() var err error
switch kind { switch v := fieldPointer.Interface().(type) {
default: case bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64:
if err := NewEncode(w, fieldPointer.Interface()); err != nil { err = binary.Write(w, binary.BigEndian, v)
fmt.Printf("unable to convert value to [%s][%s]\n", fieldPointer.Type().Kind(), field.Name) case string:
if err = binary.Write(w, binary.BigEndian, uint32(fieldPointer.Len())); err == nil {
_, err = w.Write([]byte(v))
} }
case reflect.Struct: case time.Time:
if err := NewEncode(w, fieldPointer.Interface()); err != nil { 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 return err
} }
err = NewEncode(w, v)
}
if err != nil {
return err
} }
} }
} }
return nil
}
valuePtr := value.Elem() func NewDecode(r io.Reader, body any) error {
switch kind { if body == nil {
default: return nil
break }
case reflect.String, reflect.Slice:
content := valuePtr.Bytes() elem := reflect.TypeOf(body).Elem()
if err := binary.Write(w, binary.BigEndian, int64(len(content))); err != nil { valuePtr := reflect.ValueOf(body).Elem()
return err fmt.Println(elem.String())
} else if err := binary.Write(w, binary.BigEndian, content); err != nil {
return err switch elem.Kind() {
} case reflect.Struct:
case for i := range valuePtr.NumField() {
reflect.Float32, reflect.Float64, field := elem.Field(i)
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, fieldPointer := valuePtr.FieldByName(field.Name)
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: fmt.Printf("Decode: %s: %s\n", field.Name, field.Type.String())
if err := binary.Write(w, binary.BigEndian, valuePtr.Interface()); err != nil {
return err 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, &timestap); 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 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
}

View 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
}
}