WIP: Update struct decode and encode #2

Draft
Sirherobrine23 wants to merge 13 commits from struct-serealize into main
2 changed files with 250 additions and 183 deletions
Showing only changes of commit b0e4aee3ff - Show all commits

View File

@@ -1,177 +1,242 @@
package structcode package structcode
import ( import (
"bytes" "encoding"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"time"
) )
/* /*
Este bloco de codigo server apenas para facilitar minha vida no mundo do go 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 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 Os dados serão escritos em BigEndian, então tenha cuidado com os dados inseridos casos sejam inportantes
Os pointers serão verificados se são nil's para idicar para a sereliazação e desereliazação
time.Time -> time.Time.UnixMilli() *any -> int8(0|1) + data...
[]bytes = Int (Size) + []bytes []any -> int64 (Size) + data...
String -> Int (Size) + []bytes map[any]any -> int64 (Size) + (key + data)...
bool -> 0/1 []bytes Or String -> Int64 (Size) + []bytes
int, uint, int8, uint8 = int int64, uint64 -> int64
int32, uint32 = int32 int32, uint32 -> int32
int64, uint64 = int64 int16, uint16 -> int16
int8, uint8 -> int8
bool -> int8(0|1)
*/ */
var ( const selectorTagName = "ser"
typeofByte = reflect.TypeOf(([]byte{0})[0])
)
func NewEncode(w io.Writer, body any) error { var typeofBytes = reflect.TypeOf([]byte{})
if body == nil {
return nil func NewEncode(w io.Writer, target any) error {
if target == nil {
return binary.Write(w, binary.BigEndian, int8(0))
} }
targetReflect := reflect.ValueOf(target)
switch targetReflect.Type().Kind() {
case reflect.Array, reflect.Slice:
if err := binary.Write(w, binary.BigEndian, int64(targetReflect.Len())); err != nil {
return err
}
elem := reflect.TypeOf(body).Elem() switch targetReflect.Type().Elem().Kind() {
valuePtr := reflect.ValueOf(body).Elem() case typeofBytes.Elem().Kind(): // Check if the element is a byte type
buff := make([]byte, targetReflect.Len())
switch elem.Kind() { for i := range targetReflect.Len() {
case reflect.Struct: buff[i] = targetReflect.Index(i).Interface().(byte)
for i := range valuePtr.NumField() { }
field := elem.Field(i) _, err := w.Write(buff)
fieldPointer := valuePtr.FieldByName(field.Name) return err
var err error default:
switch v := fieldPointer.Interface().(type) { for i := range targetReflect.Len() {
case bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64: if err := NewEncode(w, targetReflect.Index(i).Interface()); err != nil {
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 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 return err
} }
err = NewEncode(w, v) }
}
case reflect.Pointer:
return NewEncode(w, targetReflect.Elem().Interface()) // Ignore point and reencode
case reflect.Struct:
BinaryMarshaler, isBinaryMarshaler := targetReflect.Interface().(encoding.BinaryMarshaler)
TextMarshaler, isTextMarshaler := targetReflect.Interface().(encoding.TextMarshaler)
if isBinaryMarshaler || isTextMarshaler {
var data []byte
var err error
if isBinaryMarshaler {
data, err = BinaryMarshaler.MarshalBinary()
} else {
data, err = TextMarshaler.MarshalText()
} }
if err != nil { if err != nil {
return err return err
} }
return NewEncode(w, data)
} }
}
return nil
}
func NewDecode(r io.Reader, body any) error { typeof := targetReflect.Type()
if body == nil { for i := range targetReflect.NumField() {
return nil if tag := typeof.Field(i).Tag.Get(selectorTagName); tag == "-" || !targetReflect.Field(i).IsValid() {
} continue
} else if targetReflect.Field(i).Type().Kind() == reflect.Pointer {
elem := reflect.TypeOf(body).Elem() if targetReflect.IsZero() || !targetReflect.CanInterface() || targetReflect.Field(i).IsNil() {
valuePtr := reflect.ValueOf(body).Elem() return binary.Write(w, binary.BigEndian, int8(0))
fmt.Println(elem.String()) } else if err := binary.Write(w, binary.BigEndian, int8(1)); err != nil {
return err
switch elem.Kind() { } else if err := NewEncode(w, targetReflect.Field(i).Elem().Interface()); err != nil {
case reflect.Struct: return err
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 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 := NewEncode(w, targetReflect.Field(i).Interface()); err != nil {
if err != nil {
return err return err
} }
fieldPointer.Set(reflect.ValueOf(newValue).Elem())
} }
case reflect.String:
if err := binary.Write(w, binary.BigEndian, int64(targetReflect.Len())); err != nil {
return err
}
_, err := w.Write([]byte(targetReflect.String()))
return err
case reflect.Bool:
if targetReflect.Bool() {
return binary.Write(w, binary.BigEndian, int8(1))
}
return binary.Write(w, binary.BigEndian, int8(0))
case reflect.Uint8, reflect.Int8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64, reflect.Float32, reflect.Float64:
return binary.Write(w, binary.BigEndian, target)
default:
return fmt.Errorf("set valid struct or array/slice, or any primary values")
} }
return nil return nil
} }
func Unmashall(b []byte, target any) error { func NewDecode(r io.Reader, target any) error {
return NewDecode(bytes.NewReader(b), target) if target == nil {
} return binary.Read(r, binary.BigEndian, int8(0))
}
targetReflect := reflect.ValueOf(target).Elem()
switch targetReflect.Type().Kind() {
case reflect.Slice:
var size int64
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return err
}
func Marshall(target any) ([]byte, error) { switch targetReflect.Type().Elem().Kind() {
buff := new(bytes.Buffer) case typeofBytes.Elem().Kind():
err := NewEncode(buff, target) buff := make([]byte, size)
return buff.Bytes(), err if _, err := r.Read(buff); err != nil {
return err
}
targetReflect.SetBytes(buff)
default:
for i := range int(size) {
data := reflect.New(targetReflect.Field(i).Type().Elem()).Interface()
if err := NewDecode(r, data); err != nil {
return err
}
targetReflect.Set(reflect.AppendSlice(targetReflect, reflect.ValueOf(data)))
}
}
return nil
case reflect.Array:
var size int64
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return err
} else if size != int64(targetReflect.Len()) {
return fmt.Errorf("size mismatch: expected %d, got %d", targetReflect.Len(), size)
}
switch targetReflect.Type().Elem().Kind() {
case typeofBytes.Elem().Kind(): // Check if the element is a byte type
buff := make([]byte, size)
if _, err := r.Read(buff); err != nil {
return err
}
for i, data := range buff {
targetReflect.Index(i).Set(reflect.ValueOf(data))
}
default:
for i := 0; i < int(size); i++ {
elem := reflect.New(targetReflect.Type().Elem()).Interface()
if err := NewDecode(r, elem); err != nil {
return err
}
targetReflect.Index(i).Set(reflect.ValueOf(elem).Elem())
}
}
return nil
case reflect.String:
var err error
var size int64
if err = binary.Read(r, binary.BigEndian, &size); err == nil {
buff := make([]byte, size)
if _, err = r.Read(buff); err == nil {
targetReflect.SetString(string(buff))
}
}
return err
case reflect.Bool:
var boolInt int8
if err := binary.Read(r, binary.BigEndian, &boolInt); err != nil {
return err
}
targetReflect.SetBool(boolInt == 1)
case reflect.Uint8, reflect.Int8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64, reflect.Float32, reflect.Float64:
return binary.Read(r, binary.BigEndian, target)
case reflect.Struct:
BinaryMarshaler, isBinaryMarshaler := target.(encoding.BinaryUnmarshaler)
TextMarshaler, isTextMarshaler := target.(encoding.TextUnmarshaler)
if isBinaryMarshaler || isTextMarshaler {
var data []byte
var err error
if err = NewDecode(r, &data); err != nil {
return err
} else if isBinaryMarshaler {
return BinaryMarshaler.UnmarshalBinary(data)
}
return TextMarshaler.UnmarshalText(data)
}
for i := range targetReflect.NumField() {
if tag := targetReflect.Type().Field(i).Tag.Get(selectorTagName); tag == "-" || !targetReflect.Field(i).CanSet() {
continue
}
if targetReflect.Field(i).Type().Kind() == reflect.Pointer {
var ok bool
if err := NewDecode(r, &ok); err != nil {
return err
} else if ok {
data := reflect.New(targetReflect.Field(i).Type().Elem()).Interface()
if err := NewDecode(r, data); err != nil {
return err
}
targetReflect.Field(i).Set(reflect.ValueOf(data))
}
continue
}
data := reflect.New(targetReflect.Field(i).Type()).Interface()
if err := NewDecode(r, data); err != nil {
return err
}
targetReflect.Field(i).Set(reflect.ValueOf(data).Elem())
}
case reflect.Interface:
BinaryMarshaler, isBinaryMarshaler := target.(encoding.BinaryUnmarshaler)
TextMarshaler, isTextMarshaler := target.(encoding.TextUnmarshaler)
if isBinaryMarshaler || isTextMarshaler {
var data []byte
var err error
if err = NewDecode(r, &data); err != nil {
return err
} else if isBinaryMarshaler {
return BinaryMarshaler.UnmarshalBinary(data)
}
return TextMarshaler.UnmarshalText(data)
}
default:
return fmt.Errorf("set valid struct or array/slice, or any primary values, kind %s", targetReflect.Type().Kind())
}
return nil
} }

View File

@@ -2,66 +2,68 @@ package structcode
import ( import (
"bytes" "bytes"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"testing" "testing"
"time" "time"
) )
type testBase struct { type structTest struct {
Text string Text string
Date time.Time Int8 int8
Body []byte Int16 int16
T1 float32 Int32 int32
T2 int8 Int64 int64
T3 bool
T4 *testBase Bytes []byte
ArrayBytes [4]byte
Date time.Time
Pointer *structTest
Pointer2 any
} }
func TestDecodeEncode(t *testing.T) { func TestSerelelize(t *testing.T) {
var v1, v2 testBase var testData structTest
v1.Text = "Golang is best programer language" testData.Text = "Golang is best"
v1.Body = make([]byte, 20) testData.Int8 = 2
rand.Read(v1.Body) testData.Int16 = 200
v1.Date = time.Now() testData.Int32 = 1_000_000
v1.T1 = 0.2 testData.Int64 = 1024 * 12 * 12
v1.T2 = 1 testData.Bytes = []byte("google maintener go")
v1.T4 = &testBase{ testData.ArrayBytes = [4]byte{0, 1, 1, 1}
Text: "Test t4", testData.Date = time.Now()
testData.Pointer = &structTest{
Text: "Golang",
} }
v1Body, err := Marshall(&v1) buff := new(bytes.Buffer)
if err != nil { if err := NewEncode(buff, &testData); err != nil {
t.Error(err) t.Error(err)
return return
} }
t.Log("Unsmarhall") }
t.Logf("Data: %q", base64.StdEncoding.EncodeToString(v1Body))
if err := Unmashall(v1Body, &v2); err != nil { func TestDeserelelize(t *testing.T) {
var testData structTest
testData.Text = "Golang is best"
testData.Int8 = 2
testData.Int16 = 200
testData.Int32 = 1_000_000
testData.Int64 = 1024 * 12 * 12
testData.Bytes = []byte("google maintener go")
testData.ArrayBytes = [4]byte{0, 1, 1, 1}
testData.Date = time.Now()
testData.Pointer = &structTest{
Text: "Golang",
}
buff := new(bytes.Buffer)
if err := NewEncode(buff, &testData); err != nil {
t.Error(err) t.Error(err)
return return
} else if v2.Text != v1.Text { }
t.Errorf("invalid unmarshall data, current %q, accept %q", v2.Text, v1.Text) var decodeTest structTest
return if err := NewDecode(buff, &decodeTest); err != nil {
} else if !bytes.Equal(v2.Body, v1.Body) { t.Error(err)
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 return
} }
} }