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
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, &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
}
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
}
}