WIP: Update struct decode and encode #2

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

View File

@ -1,177 +1,242 @@
package structcode
import (
"bytes"
"encoding"
"encoding/binary"
"fmt"
"io"
"reflect"
"strings"
"time"
)
/*
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
Os pointers serão verificados se são nil's para idicar para a sereliazação e desereliazação
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
*any -> int8(0|1) + data...
[]any -> int64 (Size) + data...
map[any]any -> int64 (Size) + (key + data)...
[]bytes Or String -> Int64 (Size) + []bytes
int64, uint64 -> int64
int32, uint32 -> int32
int16, uint16 -> int16
int8, uint8 -> int8
bool -> int8(0|1)
*/
var (
typeofByte = reflect.TypeOf(([]byte{0})[0])
)
const selectorTagName = "ser"
func NewEncode(w io.Writer, body any) error {
if body == nil {
return nil
var typeofBytes = reflect.TypeOf([]byte{})
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()
valuePtr := reflect.ValueOf(body).Elem()
switch elem.Kind() {
case reflect.Struct:
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)
switch targetReflect.Type().Elem().Kind() {
case typeofBytes.Elem().Kind(): // Check if the element is a byte type
buff := make([]byte, targetReflect.Len())
for i := range targetReflect.Len() {
buff[i] = targetReflect.Index(i).Interface().(byte)
}
_, err := w.Write(buff)
return err
default:
if fieldPointer.IsNil() {
if err = binary.Write(w, binary.BigEndian, uint8(0)); err != nil {
for i := range targetReflect.Len() {
if err := NewEncode(w, targetReflect.Index(i).Interface()); err != nil {
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 {
return err
}
return NewEncode(w, data)
}
typeof := targetReflect.Type()
for i := range targetReflect.NumField() {
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
}
}
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
}
func NewDecode(r io.Reader, body any) error {
if body == nil {
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
}
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)
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 size uint32
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 {
fieldPointer.SetString(string(buff))
continue
targetReflect.SetString(string(buff))
}
}
case reflect.Array: // [2]any
if fieldPointer.Field(0).Type().String() == typeofByte.String() {
if _, err = r.Read(newValue.([]byte)); err != nil {
return err
case reflect.Bool:
var boolInt int8
if err := binary.Read(r, binary.BigEndian, &boolInt); 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 {
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)
}
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
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
}
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
}
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 (
"bytes"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"testing"
"time"
)
type testBase struct {
type structTest struct {
Text string
Int8 int8
Int16 int16
Int32 int32
Int64 int64
Bytes []byte
ArrayBytes [4]byte
Date time.Time
Body []byte
T1 float32
T2 int8
T3 bool
T4 *testBase
Pointer *structTest
Pointer2 any
}
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",
func TestSerelelize(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",
}
v1Body, err := Marshall(&v1)
if err != nil {
buff := new(bytes.Buffer)
if err := NewEncode(buff, &testData); err != nil {
t.Error(err)
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)
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)
}
var decodeTest structTest
if err := NewDecode(buff, &decodeTest); err != nil {
t.Error(err)
return
}
}