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)
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 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)
} }
_, err := w.Write(buff)
return err
default: default:
if fieldPointer.IsNil() { for i := range targetReflect.Len() {
if err = binary.Write(w, binary.BigEndian, uint8(0)); err != nil { if err := NewEncode(w, targetReflect.Index(i).Interface()); err != nil {
return err return err
} }
continue
} }
if err = binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
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 {
if targetReflect.IsZero() || !targetReflect.CanInterface() || targetReflect.Field(i).IsNil() {
return binary.Write(w, binary.BigEndian, int8(0))
} else if err := binary.Write(w, binary.BigEndian, int8(1)); err != nil {
return err
} else if err := NewEncode(w, targetReflect.Field(i).Elem().Interface()); err != nil {
return err
}
continue
}
if err := NewEncode(w, targetReflect.Field(i).Interface()); err != nil {
return err
}
} }
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: case reflect.String:
var size uint32 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
}
func NewDecode(r io.Reader, target any) error {
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
}
switch targetReflect.Type().Elem().Kind() {
case typeofBytes.Elem().Kind():
buff := make([]byte, size)
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 { if err = binary.Read(r, binary.BigEndian, &size); err == nil {
buff := make([]byte, size) buff := make([]byte, size)
if _, err = r.Read(buff); err == nil { if _, err = r.Read(buff); err == nil {
fieldPointer.SetString(string(buff)) targetReflect.SetString(string(buff))
continue
} }
} }
case reflect.Array: // [2]any return err
if fieldPointer.Field(0).Type().String() == typeofByte.String() { case reflect.Bool:
if _, err = r.Read(newValue.([]byte)); err != nil { var boolInt int8
if err := binary.Read(r, binary.BigEndian, &boolInt); err != nil {
return err return err
} }
fieldPointer.Set(reflect.Append(fieldPointer.Elem(), reflect.ValueOf(newValue))) targetReflect.SetBool(boolInt == 1)
continue 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)
for i := range fieldPointer.Len() { case reflect.Struct:
newValue := reflect.New(fieldPointer.Field(i).Type()).Interface() BinaryMarshaler, isBinaryMarshaler := target.(encoding.BinaryUnmarshaler)
if err = NewDecode(r, newValue); err != nil { TextMarshaler, isTextMarshaler := target.(encoding.TextUnmarshaler)
if isBinaryMarshaler || isTextMarshaler {
var data []byte
var err error
if err = NewDecode(r, &data); err != nil {
return err return err
} else if isBinaryMarshaler {
return BinaryMarshaler.UnmarshalBinary(data)
} }
fieldPointer.Field(i).Set(reflect.ValueOf(newValue)) return TextMarshaler.UnmarshalText(data)
} }
for i := range targetReflect.NumField() {
if tag := targetReflect.Type().Field(i).Tag.Get(selectorTagName); tag == "-" || !targetReflect.Field(i).CanSet() {
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 != nil { 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 return err
} }
fieldPointer.Set(reflect.ValueOf(newValue).Elem()) 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 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

@@ -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
Int8 int8
Int16 int16
Int32 int32
Int64 int64
Bytes []byte
ArrayBytes [4]byte
Date time.Time Date time.Time
Body []byte Pointer *structTest
T1 float32 Pointer2 any
T2 int8
T3 bool
T4 *testBase
} }
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
} }
} }