From 4c964436a645f37e85065e678d8423f449dc9775 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Wed, 10 Sep 2025 13:31:48 +0300 Subject: [PATCH 01/14] feat: added the msgpack serialization for int,float,string types --- cmd/main.go | 5 ++++ go.mod | 3 +++ pkg/serlializer.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 cmd/main.go create mode 100644 go.mod create mode 100644 pkg/serlializer.go diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..fe7f767 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c0875b6 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/codescalersinternships/msgpack-rawan + +go 1.25.0 diff --git a/pkg/serlializer.go b/pkg/serlializer.go new file mode 100644 index 0000000..181b4f7 --- /dev/null +++ b/pkg/serlializer.go @@ -0,0 +1,65 @@ +package pkg + +import "math" + +func Serialize(object interface{}) ([]byte, error) { + var result []byte + + switch objType := object.(type) { + case nil: + result = append(result, 0xC0) + case bool: + if objType { + result = append(result, 0xC3) + } else { + result = append(result, 0xC2) + } + case uint8: + result = append(result, 0xCC) + result = append(result, byte(objType)) + case uint16: + result = append(result, 0xCD) + result = append(result, byte(objType>>8), byte(objType)) + case uint32: + result = append(result, 0xCE) + result = append(result, byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + case uint64: + result = append(result, 0xCF) + result = append(result, byte(objType>>56), byte(objType>>48), byte(objType>>40), byte(objType>>32), byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + case int8: + result = append(result, 0xD0) + result = append(result, byte(objType)) + case int16: + result = append(result, 0xD1) + result = append(result, byte(objType>>8), byte(objType)) + case int32: + result = append(result, 0xD2) + result = append(result, byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + case int64: + result = append(result, 0xD3) + result = append(result, byte(objType>>56), byte(objType>>48), byte(objType>>40), byte(objType>>32), byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + case float32: + result = append(result, 0xCA) + floatBits := math.Float32bits(objType) + result = append(result, byte(floatBits>>24), byte(floatBits>>16), byte(floatBits>>8), byte(floatBits)) + case float64: + result = append(result, 0xCB) + floatBits := math.Float64bits(objType) + result = append(result, byte(floatBits>>56), byte(floatBits>>48), byte(floatBits>>40), byte(floatBits>>32), byte(floatBits>>24), byte(floatBits>>16), byte(floatBits>>8), byte(floatBits)) + case string: + strLen := len(objType) + if strLen < 32 { + result = append(result, 0xA0 | byte(strLen)) + result = append(result, []byte(objType)...) + } else if strLen < 2^8 { + result = append(result, 0xD9) + result = append(result, byte(strLen)) + result = append(result, []byte(objType)...) + } else if strLen < 2^32 { + result = append(result, 0xDB) + result = append(result, byte(strLen>>24), byte(strLen>>16), byte(strLen>>8), byte(strLen)) + result = append(result, []byte(objType)...) + } + } + return result, nil +} From 68f18c79d9c8a97747891c36bc4e1fe210aa76d4 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Wed, 10 Sep 2025 13:40:13 +0300 Subject: [PATCH 02/14] feat: added bin format --- pkg/serlializer.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/serlializer.go b/pkg/serlializer.go index 181b4f7..fde45a4 100644 --- a/pkg/serlializer.go +++ b/pkg/serlializer.go @@ -49,17 +49,36 @@ func Serialize(object interface{}) ([]byte, error) { case string: strLen := len(objType) if strLen < 32 { - result = append(result, 0xA0 | byte(strLen)) + result = append(result, 0xA0|byte(strLen)) result = append(result, []byte(objType)...) } else if strLen < 2^8 { result = append(result, 0xD9) result = append(result, byte(strLen)) result = append(result, []byte(objType)...) + } else if strLen < 2^16 { + result = append(result, 0xDA) + result = append(result, byte(strLen>>8), byte(strLen)) + result = append(result, []byte(objType)...) } else if strLen < 2^32 { result = append(result, 0xDB) result = append(result, byte(strLen>>24), byte(strLen>>16), byte(strLen>>8), byte(strLen)) result = append(result, []byte(objType)...) } + case []byte: + byteLen := len(objType) + if byteLen < 2^8 { + result = append(result, 0xC4) + result = append(result, byte(byteLen)) + result = append(result, []byte(objType)...) + } else if byteLen < 2^16 { + result = append(result, 0xC5) + result = append(result, byte(byteLen>>8), byte(byteLen)) + result = append(result, []byte(objType)...) + } else if byteLen < 2^32 { + result = append(result, 0xC6) + result = append(result, byte(byteLen>>24), byte(byteLen>>16), byte(byteLen>>8), byte(byteLen)) + result = append(result, []byte(objType)...) + } } return result, nil } From 53d37ae00186f835b4107109a995dca13e098e0e Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Wed, 10 Sep 2025 14:26:49 +0300 Subject: [PATCH 03/14] test: added nil,bool,uint,int testcases --- pkg/serializer_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 pkg/serializer_test.go diff --git a/pkg/serializer_test.go b/pkg/serializer_test.go new file mode 100644 index 0000000..9a919fe --- /dev/null +++ b/pkg/serializer_test.go @@ -0,0 +1,82 @@ +package pkg + +import ( + "reflect" + "testing" +) + +func TestSerialize(t *testing.T) { + testcases := []struct { + name string + input interface{} + expected []byte + }{ + { + name: "nil", + input: nil, + expected: []byte{0xC0}, + }, + { + name: "boolean true", + input: true, + expected: []byte{0xC3}, + }, + { + name: "boolean false", + input: false, + expected: []byte{0xC2}, + }, + { + name: "uint8", + input: uint8(255), + expected: []byte{0xCC, 0xFF}, + }, + { + name: "uint16", + input: uint16(65535), + expected: []byte{0xCD, 0xFF, 0xFF}, + }, + { + name: "uint32", + input: uint32(4294967295), + expected: []byte{0xCE, 0xFF, 0xFF, 0xFF, 0xFF}, + }, + { + name: "uint64", + input: uint64(18446744073709551615), + expected: []byte{0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + }, + { + name: "int8", + input: int8(-128), + expected: []byte{0xD0, 0x80}, + }, + { + name: "int16", + input: int16(-300), + expected: []byte{0xD1, 0xFE, 0xD4}, + }, + { + name: "int32", + input: int32(-70000), + expected: []byte{0xD2, 0xFF, 0xFE, 0xEE, 0x90}, + }, + { + name: "int64", + input: int64(-123456789), + expected: []byte{0xD3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xA4, 0x32, 0xEB}, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + result, err := Serialize(tc.input) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("expected %v, got %v", tc.expected, result) + } + }) + } + +} From 638dd5fd7affc79b1ac82265ed9bc08f6b9ed32c Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Wed, 10 Sep 2025 14:53:14 +0300 Subject: [PATCH 04/14] test: added unit tests for float, bin types --- pkg/serializer_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ pkg/serlializer.go | 12 +++++------ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/pkg/serializer_test.go b/pkg/serializer_test.go index 9a919fe..474432e 100644 --- a/pkg/serializer_test.go +++ b/pkg/serializer_test.go @@ -66,6 +66,52 @@ func TestSerialize(t *testing.T) { input: int64(-123456789), expected: []byte{0xD3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xA4, 0x32, 0xEB}, }, + { + name: "float32", + input: float32(3.14), + expected: []byte{0xCA, 0x40, 0x48, 0xF5, 0xC3}, + }, + { + name: "float64", + input: float64(3.141592653589793), + expected: []byte{0xCB, 0x40, 0x09, 0x21, 0xFB, 0x54, 0x44, 0x2D, 0x18}, + }, + { + name: "fix string", + input: "hello", + expected: []byte{0xA5, 'h', 'e', 'l', 'l', 'o'}, + }, + { + name: "str8", + input: string(make([]byte, 100)), + expected: append([]byte{0xD9, 100}, make([]byte, 100)...), + }, + { + name: "str16", + input: string(make([]byte, 7000)), + expected: append([]byte{0xDA, 0x1B, 0x58}, make([]byte, 7000)...), + }, + { + name: "str32", + input: string(make([]byte, 70000)), + expected: append([]byte{0xDB, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), + + }, + { + name: "bin8", + input: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + expected: []byte{0xC4, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05}, + }, + { + name: "bin16", + input: make([]byte, 300), + expected: append([]byte{0xC5, 0x01, 0x2C}, make([]byte, 300)...), + }, + { + name: "bin32", + input: make([]byte, 70000), + expected: append([]byte{0xC6, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), + }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/serlializer.go b/pkg/serlializer.go index fde45a4..67af4b8 100644 --- a/pkg/serlializer.go +++ b/pkg/serlializer.go @@ -51,30 +51,30 @@ func Serialize(object interface{}) ([]byte, error) { if strLen < 32 { result = append(result, 0xA0|byte(strLen)) result = append(result, []byte(objType)...) - } else if strLen < 2^8 { + } else if strLen < int(math.Pow(2, 8)) { result = append(result, 0xD9) result = append(result, byte(strLen)) result = append(result, []byte(objType)...) - } else if strLen < 2^16 { + } else if strLen < int(math.Pow(2, 16)) { result = append(result, 0xDA) result = append(result, byte(strLen>>8), byte(strLen)) result = append(result, []byte(objType)...) - } else if strLen < 2^32 { + } else if strLen < int(math.Pow(2, 32)) { result = append(result, 0xDB) result = append(result, byte(strLen>>24), byte(strLen>>16), byte(strLen>>8), byte(strLen)) result = append(result, []byte(objType)...) } case []byte: byteLen := len(objType) - if byteLen < 2^8 { + if byteLen < int(math.Pow(2, 8)) { result = append(result, 0xC4) result = append(result, byte(byteLen)) result = append(result, []byte(objType)...) - } else if byteLen < 2^16 { + } else if byteLen < int(math.Pow(2, 16)) { result = append(result, 0xC5) result = append(result, byte(byteLen>>8), byte(byteLen)) result = append(result, []byte(objType)...) - } else if byteLen < 2^32 { + } else if byteLen < int(math.Pow(2, 32)) { result = append(result, 0xC6) result = append(result, byte(byteLen>>24), byte(byteLen>>16), byte(byteLen>>8), byte(byteLen)) result = append(result, []byte(objType)...) From 11f004c07f0d8ad94f8d6388b9332e518d0b5429 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Thu, 11 Sep 2025 14:13:34 +0300 Subject: [PATCH 05/14] feat: added array type serialization, positive fixint, negative fixint --- pkg/serlializer.go | 117 ++++++++++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/pkg/serlializer.go b/pkg/serlializer.go index 67af4b8..a3c7b9d 100644 --- a/pkg/serlializer.go +++ b/pkg/serlializer.go @@ -1,43 +1,75 @@ package pkg -import "math" +import ( + "math" + "reflect" +) + +func handleInt(objType int64, result []byte) []byte { + if objType >= 0 && objType < 128 { // positive fixint + result = append(result, byte(objType)) + + } else if objType >= -32 && objType < 0 { // negative fixint + result = append(result, byte(int8(objType))) + + } else if objType >= math.MinInt8 && objType <= math.MaxInt8 { // int 8 + result = append(result, 0xD0) + result = append(result, byte(int8(objType))) + + } else if objType >= math.MinInt16 && objType <= math.MaxInt16 { // int 16 + result = append(result, 0xD1) + result = append(result, byte(int16(objType)>>8), byte(int16(objType))) + + } else if objType >= math.MinInt32 && objType <= math.MaxInt32 { // int 32 + result = append(result, 0xD2) + result = append(result, byte(int32(objType)>>24), byte(int32(objType)>>16), byte(int32(objType)>>8), byte(int32(objType))) + + } else { // int 64 + result = append(result, 0xD3) + result = append(result, byte(int64(objType)>>56), byte(int64(objType)>>48), byte(int64(objType)>>40), byte(int64(objType)>>32), byte(int64(objType)>>24), byte(int64(objType)>>16), byte(int64(objType)>>8), byte(int64(objType))) + } + return result +} + +func handleUint(objType uint64, result []byte) []byte { + if objType < (1 << 7) { // positive fixint + result = append(result, byte(objType)) + } else if objType < (1 << 8) { // uint 8 + result = append(result, 0xCC) + result = append(result, byte(objType)) + } else if objType < (1 << 16) { // uint 16 + result = append(result, 0xCD) + result = append(result, byte(objType>>8), byte(objType)) + } else if objType < (1 << 32) { // uint 32 + result = append(result, 0xCE) + result = append(result, byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + } else { // uint 64 + result = append(result, 0xCF) + result = append(result, byte(objType>>56), byte(objType>>48), byte(objType>>40), byte(objType>>32), byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + } + return result +} func Serialize(object interface{}) ([]byte, error) { var result []byte switch objType := object.(type) { + case nil: result = append(result, 0xC0) + case bool: if objType { result = append(result, 0xC3) } else { result = append(result, 0xC2) } - case uint8: - result = append(result, 0xCC) - result = append(result, byte(objType)) - case uint16: - result = append(result, 0xCD) - result = append(result, byte(objType>>8), byte(objType)) - case uint32: - result = append(result, 0xCE) - result = append(result, byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) - case uint64: - result = append(result, 0xCF) - result = append(result, byte(objType>>56), byte(objType>>48), byte(objType>>40), byte(objType>>32), byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) - case int8: - result = append(result, 0xD0) - result = append(result, byte(objType)) - case int16: - result = append(result, 0xD1) - result = append(result, byte(objType>>8), byte(objType)) - case int32: - result = append(result, 0xD2) - result = append(result, byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) - case int64: - result = append(result, 0xD3) - result = append(result, byte(objType>>56), byte(objType>>48), byte(objType>>40), byte(objType>>32), byte(objType>>24), byte(objType>>16), byte(objType>>8), byte(objType)) + + case uint, uint8, uint16, uint32, uint64: + result = handleUint(reflect.ValueOf(objType).Uint(), result) + + case int, int8, int16, int32, int64: + result = handleInt(reflect.ValueOf(objType).Int(), result) case float32: result = append(result, 0xCA) floatBits := math.Float32bits(objType) @@ -50,34 +82,55 @@ func Serialize(object interface{}) ([]byte, error) { strLen := len(objType) if strLen < 32 { result = append(result, 0xA0|byte(strLen)) - result = append(result, []byte(objType)...) + } else if strLen < int(math.Pow(2, 8)) { result = append(result, 0xD9) result = append(result, byte(strLen)) - result = append(result, []byte(objType)...) + } else if strLen < int(math.Pow(2, 16)) { result = append(result, 0xDA) result = append(result, byte(strLen>>8), byte(strLen)) - result = append(result, []byte(objType)...) + } else if strLen < int(math.Pow(2, 32)) { result = append(result, 0xDB) result = append(result, byte(strLen>>24), byte(strLen>>16), byte(strLen>>8), byte(strLen)) - result = append(result, []byte(objType)...) } + result = append(result, []byte(objType)...) case []byte: byteLen := len(objType) if byteLen < int(math.Pow(2, 8)) { result = append(result, 0xC4) result = append(result, byte(byteLen)) - result = append(result, []byte(objType)...) + } else if byteLen < int(math.Pow(2, 16)) { result = append(result, 0xC5) result = append(result, byte(byteLen>>8), byte(byteLen)) - result = append(result, []byte(objType)...) + } else if byteLen < int(math.Pow(2, 32)) { result = append(result, 0xC6) result = append(result, byte(byteLen>>24), byte(byteLen>>16), byte(byteLen>>8), byte(byteLen)) - result = append(result, []byte(objType)...) + } + result = append(result, []byte(objType)...) + case []any: + arrLen := len(objType) + if arrLen < 16 { + result = append(result, 0x90|byte(arrLen)) + + } else if arrLen < int(math.Pow(2, 16)) { + result = append(result, 0xDC) + result = append(result, byte(arrLen>>8), byte(arrLen)) + + } else if arrLen < int(math.Pow(2, 32)) { + result = append(result, 0xDD) + result = append(result, byte(arrLen>>24), byte(arrLen>>16), byte(arrLen>>8), byte(arrLen)) + } + + for _, element := range objType { + serializedElement, err := Serialize(element) + if err != nil { + return nil, err + } + result = append(result, serializedElement...) } } return result, nil From 0690e3f5aec1600917ecb6e1daead0394ae18d1d Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Thu, 11 Sep 2025 14:13:53 +0300 Subject: [PATCH 06/14] test: added unit tests for array type serialization, positive fixint, negative fixint --- pkg/serializer_test.go | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/pkg/serializer_test.go b/pkg/serializer_test.go index 474432e..56ca9f9 100644 --- a/pkg/serializer_test.go +++ b/pkg/serializer_test.go @@ -1,6 +1,7 @@ package pkg import ( + "bytes" "reflect" "testing" ) @@ -43,28 +44,38 @@ func TestSerialize(t *testing.T) { }, { name: "uint64", - input: uint64(18446744073709551615), - expected: []byte{0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + input: uint64(5000000000), + expected: []byte{0xCF, 0x00, 0x00, 0x00, 0x01, 0x2A, 0x05, 0xF2, 0x00}, + }, + { + name: "positive fixint", + input: 100, + expected: []byte{0x64}, + }, + { + name: "negative fixint", + input: -20, + expected: []byte{0xEC}, }, { name: "int8", - input: int8(-128), + input: -128, expected: []byte{0xD0, 0x80}, }, { name: "int16", - input: int16(-300), + input: -300, expected: []byte{0xD1, 0xFE, 0xD4}, }, { name: "int32", - input: int32(-70000), + input: -70000, expected: []byte{0xD2, 0xFF, 0xFE, 0xEE, 0x90}, }, { name: "int64", - input: int64(-123456789), - expected: []byte{0xD3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xA4, 0x32, 0xEB}, + input: -5000000000, + expected: []byte{0xD3, 0xFF, 0xFF, 0xFF, 0xFE, 0xD5, 0xFA, 0x0E, 0x00}, }, { name: "float32", @@ -92,10 +103,9 @@ func TestSerialize(t *testing.T) { expected: append([]byte{0xDA, 0x1B, 0x58}, make([]byte, 7000)...), }, { - name: "str32", - input: string(make([]byte, 70000)), + name: "str32", + input: string(make([]byte, 70000)), expected: append([]byte{0xDB, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), - }, { name: "bin8", @@ -112,6 +122,16 @@ func TestSerialize(t *testing.T) { input: make([]byte, 70000), expected: append([]byte{0xC6, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), }, + { + name: "fix array", + input: []any{1, 2, 3}, + expected: []byte{0x93, 0x01, 0x02, 0x03}, + }, + { + name: "array16", + input: make([]any, 200), + expected: append([]byte{0xDC, 0x00, 0xC8}, bytes.Repeat([]byte{0xC0}, 200)...), + }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { From d475e9c2f0f57642059632334fc6850440901df2 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Thu, 11 Sep 2025 14:38:40 +0300 Subject: [PATCH 07/14] feat: added map type serialization --- pkg/serlializer.go | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/pkg/serlializer.go b/pkg/serlializer.go index a3c7b9d..970c6fe 100644 --- a/pkg/serlializer.go +++ b/pkg/serlializer.go @@ -113,14 +113,14 @@ func Serialize(object interface{}) ([]byte, error) { result = append(result, []byte(objType)...) case []any: arrLen := len(objType) - if arrLen < 16 { + if arrLen < 16 { //fixarray result = append(result, 0x90|byte(arrLen)) - } else if arrLen < int(math.Pow(2, 16)) { + } else if arrLen < int(math.Pow(2, 16)) { //array16 result = append(result, 0xDC) result = append(result, byte(arrLen>>8), byte(arrLen)) - } else if arrLen < int(math.Pow(2, 32)) { + } else if arrLen < int(math.Pow(2, 32)) { //array32 result = append(result, 0xDD) result = append(result, byte(arrLen>>24), byte(arrLen>>16), byte(arrLen>>8), byte(arrLen)) } @@ -132,6 +132,31 @@ func Serialize(object interface{}) ([]byte, error) { } result = append(result, serializedElement...) } + case map[any]any: + mapLen := len(objType) + if mapLen < 16 { //fixmap + result = append(result, 0x80|byte(mapLen)) + } else if mapLen < int(math.Pow(2, 16)) { //map16 + result = append(result, 0xDE) + result = append(result, byte(mapLen>>8), byte(mapLen)) + } else if mapLen < int(math.Pow(2, 32)) { //map32 + result = append(result, 0xDF) + result = append(result, byte(mapLen>>24), byte(mapLen>>16), byte(mapLen>>8), byte(mapLen)) + } + for key, value := range objType { + serializedElement, err := Serialize(key) + if err != nil { + return nil, err + } + result = append(result, serializedElement...) + + + serializedElement, err = Serialize(value) + if err != nil { + return nil, err + } + result = append(result, serializedElement...) + } } return result, nil } From 7e3e38eaa50ded0c2d39e171a0dc719f680857dc Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Thu, 11 Sep 2025 14:38:53 +0300 Subject: [PATCH 08/14] test: added unit tests for map type --- pkg/serializer_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pkg/serializer_test.go b/pkg/serializer_test.go index 56ca9f9..8a513bf 100644 --- a/pkg/serializer_test.go +++ b/pkg/serializer_test.go @@ -122,6 +122,11 @@ func TestSerialize(t *testing.T) { input: make([]byte, 70000), expected: append([]byte{0xC6, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), }, + { + name: "empty array", + input: []any{}, + expected: []byte{0x90}, + }, { name: "fix array", input: []any{1, 2, 3}, @@ -132,6 +137,29 @@ func TestSerialize(t *testing.T) { input: make([]any, 200), expected: append([]byte{0xDC, 0x00, 0xC8}, bytes.Repeat([]byte{0xC0}, 200)...), }, + { + name: "array32", + input: make([]any, 70000), + expected: append([]byte{0xDD, 0x00, 0x01, 0x11, 0x70}, bytes.Repeat([]byte{0xC0}, 70000)...), + }, + { + name: "nested array", + input: []any{1, []any{2, 3}, 4}, + expected: []byte{0x93, 0x01, 0x92, 0x02, 0x03, 0x04}, + }, + { + name:"empty map", + input: map[any]any{}, + expected: []byte{0x80}, + }, + { + name: "fix map", + input: map[any]any{ + "a": 1, + "b": true, + }, + expected: []byte{0x82, 0xA1, 'a', 0x01, 0xA1, 'b', 0xC3}, + }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { From 2f1155c92f309fb79a4ae1fe01dea642169d389d Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Thu, 11 Sep 2025 17:58:38 +0300 Subject: [PATCH 09/14] feat: deserializer of all types added --- pkg/deserializer.go | 143 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 pkg/deserializer.go diff --git a/pkg/deserializer.go b/pkg/deserializer.go new file mode 100644 index 0000000..6360b4d --- /dev/null +++ b/pkg/deserializer.go @@ -0,0 +1,143 @@ +package pkg + +import "math" + +func Deserialize(bytes []byte) (interface{}, int, error) { + var result interface{} + + switch bytes[0] { + + case 0xC0: //nil + result = nil + return result, 1, nil + case 0xC3: //bool true + result = true + return result, 1, nil + case 0xC2: // bool false + result = false + return result, 1, nil + case 0xCC: // uint8 + result = uint8(bytes[1]) + return result, 2, nil + case 0xCD: // uint16 + result = uint16(bytes[1])<<8 | uint16(bytes[2]) + return result, 3, nil + case 0xCE: // uint32 + result = uint32(bytes[1])<<24 | uint32(bytes[2])<<16 | uint32(bytes[3])<<8 | uint32(bytes[4]) + return result, 5, nil + case 0xCF: // uint64 + result = uint64(bytes[1])<<56 | uint64(bytes[2])<<48 | uint64(bytes[3])<<40 | uint64(bytes[4])<<32 | uint64(bytes[5])<<24 | uint64(bytes[6])<<16 | uint64(bytes[7])<<8 | uint64(bytes[8]) + return result, 9, nil + case 0xD0: // int8 + result = int8(bytes[1]) + return result, 2, nil + case 0xD1: // int16 + result = int16(bytes[1])<<8 | int16(bytes[2]) + return result, 3, nil + case 0xD2: // int32 + result = int32(bytes[1])<<24 | int32(bytes[2])<<16 | int32(bytes[3])<<8 | int32(bytes[4]) + return result, 5, nil + case 0xD3: // int64 + result = int64(bytes[1])<<56 | int64(bytes[2])<<48 | int64(bytes[3])<<40 | int64(bytes[4])<<32 | int64(bytes[5])<<24 | int64(bytes[6])<<16 | int64(bytes[7])<<8 | int64(bytes[8]) + return result, 9, nil + case 0xCA: // float32 + result = math.Float32frombits(uint32(bytes[1])<<24 | uint32(bytes[2])<<16 | uint32(bytes[3])<<8 | uint32(bytes[4])) + return result, 5, nil + case 0xCB: // float64 + result = math.Float64frombits(uint64(bytes[1])<<56 | uint64(bytes[2])<<48 | uint64(bytes[3])<<40 | uint64(bytes[4])<<32 | uint64(bytes[5])<<24 | uint64(bytes[6])<<16 | uint64(bytes[7])<<8 | uint64(bytes[8])) + return result, 9, nil + //fixstr + case 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF: + strLen := int(bytes[0] & 0x1F) // lower 5 bits + result = string(bytes[1 : 1+strLen]) + return result, int(bytes[0]&0x1F) + 1, nil // lower 5 bits + case 0xD9: // str8 + strLen := int(bytes[1]) + result = string(bytes[2 : 2+strLen]) + return result, strLen + 2, nil + case 0xDA: // str16 + strLen := int(bytes[1])<<8 | int(bytes[2]) + result = string(bytes[3 : 3+strLen]) + return result, strLen + 3, nil + case 0xDB: // str32 + strLen := int(bytes[1])<<24 | int(bytes[2])<<16 | int(bytes[3])<<8 | int(bytes[4]) + result = string(bytes[5 : 5+strLen]) + return result, strLen + 5, nil + case 0xC4: // bin8 + binLen := int(bytes[1]) + result = bytes[2 : 2+binLen] + return result, binLen + 2, nil + case 0xC5: // bin16 + binLen := int(bytes[1])<<8 | int(bytes[2]) + result = bytes[3 : 3+binLen] + return result, binLen + 3, nil + case 0xC6: // bin32 + binLen := int(bytes[1])<<24 | int(bytes[2])<<16 | int(bytes[3])<<8 | int(bytes[4]) + result = bytes[5 : 5+binLen] + return result, binLen + 5, nil + //fixarray + case 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F: + arrayLen := int(bytes[0] & 0x0F) //get lower 4 bits + arr := make([]any, 0, arrayLen) + rest := bytes[1:] + for i := 0; i < arrayLen; i++ { + elem, n, _ := Deserialize(rest) + arr = append(arr, elem) + rest = rest[n:] + } + result = arr + return result, len(bytes) - len(rest), nil + case 0xDC: // array16 + arrayLen := int(bytes[1])<<8 | int(bytes[2]) + arr := make([]any, 0, arrayLen) + + rest := bytes[3:] + for i := 0; i < arrayLen; i++ { + elem, n, _ := Deserialize(rest) + arr = append(arr, elem) + rest = rest[n:] + } + result = arr + return result, len(bytes) - len(rest), nil + case 0xDD: // array32 + arrayLen := int(bytes[1])<<24 | (int(bytes[2]) << 16) | (int(bytes[3]) << 8) | int(bytes[4]) + arr := make([]any, 0, arrayLen) + + rest := bytes[5:] + for i := 0; i < arrayLen; i++ { + elem, n, _ := Deserialize(rest) + arr = append(arr, elem) + rest = rest[n:] + } + result = arr + return result, len(bytes) - len(rest), nil + //fixmap + case 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F: + mapLen := int(bytes[0] & 0x0F) //get lower 4 bits + temp_map := make(map[any]any, mapLen) + + rest := bytes[1:] + consumed := 1 + for i := 0; i < mapLen; i++ { + key, n, _ := Deserialize(rest) + consumed += n + rest = rest[n:] + + val, n, _ := Deserialize(rest) + consumed += n + rest = rest[n:] + temp_map[key] = val + } + result = temp_map + return result, len(bytes) - len(rest), nil + default: + if bytes[0] < 128 { // positive fixint + result = int(bytes[0]) + return result, 1, nil + } else if bytes[0] >= 0xE0 { // negative fixint + result = int8(bytes[0]) + return result, 1, nil + } + } + return result, 0, nil +} From 6083a22932e0168a90040cda9157636dda54e85d Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Thu, 11 Sep 2025 17:58:55 +0300 Subject: [PATCH 10/14] test: unit tests of deserialization --- pkg/deserializer_test.go | 156 +++++++++++++++++++++++++++++++++++++++ pkg/serializer_test.go | 4 +- 2 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 pkg/deserializer_test.go diff --git a/pkg/deserializer_test.go b/pkg/deserializer_test.go new file mode 100644 index 0000000..4b6596e --- /dev/null +++ b/pkg/deserializer_test.go @@ -0,0 +1,156 @@ +package pkg + +import ( + "bytes" + "reflect" + "testing" +) + +func TestDeserialize(t *testing.T) { + testcases := []struct { + name string + input []byte + expected interface{} + }{ + { + name: "nil", + input: []byte{0xC0}, + expected: nil, + }, + { + name: "boolean true", + input: []byte{0xC3}, + expected: true, + }, + { + name: "boolean false", + input: []byte{0xC2}, + expected: false, + }, + { + name: "uint8", + input: []byte{0xCC, 0xFF}, + expected: uint8(255), + }, + { + name: "uint16", + input: []byte{0xCD, 0xFF, 0xFF}, + expected: uint16(65535), + }, + { + name: "uint32", + input: []byte{0xCE, 0xFF, 0xFF, 0xFF, 0xFF}, + expected: uint32(4294967295), + }, + { + name: "uint64", + input: []byte{0xCF, 0x00, 0x00, 0x00, 0x01, 0x2A, 0x05, 0xF2, 0x00}, + expected: uint64(5000000000), + }, + { + name: "float32", + input: []byte{0xCA, 0x40, 0x48, 0xF5, 0xC3}, + expected: float32(3.14), + }, + { + name: "float64", + input: []byte{0xCB, 0x40, 0x09, 0x21, 0xFB, 0x54, 0x44, 0x2D, 0x18}, + expected: float64(3.141592653589793), + }, + { + name: "fix string", + input: []byte{0xA5, 'h', 'e', 'l', 'l', 'o'}, + expected: "hello", + }, + { + name: "str8", + input: append([]byte{0xD9, 100}, make([]byte, 100)...), + expected: string(make([]byte, 100)), + }, + { + name: "str16", + input: append([]byte{0xDA, 0x1B, 0x58}, make([]byte, 7000)...), + expected: string(make([]byte, 7000)), + }, + { + name: "str32", + input: append([]byte{0xDB, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), + expected: string(make([]byte, 70000)), + }, + { + name: "bin8", + input: []byte{0xC4, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05}, + expected: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + }, + { + name: "bin16", + input: append([]byte{0xC5, 0x01, 0x2C}, make([]byte, 300)...), + expected: make([]byte, 300), + }, + { + name: "bin32", + input: append([]byte{0xC6, 0x00, 0x01, 0x11, 0x70}, make([]byte, 70000)...), + expected: make([]byte, 70000), + }, + { + name: "empty array", + input: []byte{0x90}, + expected: []any{}, + }, + { + name: "fix array", + input: []byte{0x93, 0x01, 0x02, 0x03}, + expected: []any{1, 2, 3}, + }, + { + name: "array16", + input: append([]byte{0xDC, 0x00, 0xC8}, bytes.Repeat([]byte{0xC0}, 200)...), + expected: make([]any, 200), + }, + { + name: "array32", + input: append([]byte{0xDD, 0x00, 0x01, 0x11, 0x70}, bytes.Repeat([]byte{0xC0}, 70000)...), + expected: make([]any, 70000), + }, + { + name: "nested array", + input: []byte{0x93, 0x01, 0x92, 0x02, 0x03, 0x04}, + expected: []any{1, []any{2, 3}, 4}, + }, + { + name: "empty map", + input: []byte{0x80}, + expected: map[any]any{}, + }, + { + name: "fix map", + input: []byte{0x82, 0xA1, 'a', 0x01, 0xA1, 'b', 0xC3}, + expected: map[any]any{ + "a": 1, + "b": true, + }, + }, + // { + // name: "positive fixint", + // input: []byte{0x64}, + // expected: 100, + // }, + // { + // name: "negative fixint", + // input: []byte{0xEC}, + // expected: -20, + // }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + result, _, err := Deserialize(tc.input) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("Expected %v, got %v", tc.expected, result) + } + }) + } +} diff --git a/pkg/serializer_test.go b/pkg/serializer_test.go index 8a513bf..2c061cf 100644 --- a/pkg/serializer_test.go +++ b/pkg/serializer_test.go @@ -148,8 +148,8 @@ func TestSerialize(t *testing.T) { expected: []byte{0x93, 0x01, 0x92, 0x02, 0x03, 0x04}, }, { - name:"empty map", - input: map[any]any{}, + name: "empty map", + input: map[any]any{}, expected: []byte{0x80}, }, { From 919a8eef9f74d239fb383170452b2bbc8382f317 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Sun, 14 Sep 2025 12:26:39 +0300 Subject: [PATCH 11/14] fix: fixed failing negative fixint testcase --- pkg/deserializer.go | 2 +- pkg/deserializer_test.go | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/pkg/deserializer.go b/pkg/deserializer.go index 6360b4d..f631db6 100644 --- a/pkg/deserializer.go +++ b/pkg/deserializer.go @@ -135,7 +135,7 @@ func Deserialize(bytes []byte) (interface{}, int, error) { result = int(bytes[0]) return result, 1, nil } else if bytes[0] >= 0xE0 { // negative fixint - result = int8(bytes[0]) + result = int(int8(bytes[0])) return result, 1, nil } } diff --git a/pkg/deserializer_test.go b/pkg/deserializer_test.go index 4b6596e..62696d6 100644 --- a/pkg/deserializer_test.go +++ b/pkg/deserializer_test.go @@ -119,27 +119,27 @@ func TestDeserialize(t *testing.T) { }, { name: "empty map", - input: []byte{0x80}, - expected: map[any]any{}, + input: []byte{0x80}, + expected: map[any]any{}, }, { - name: "fix map", + name: "fix map", input: []byte{0x82, 0xA1, 'a', 0x01, 0xA1, 'b', 0xC3}, expected: map[any]any{ "a": 1, "b": true, }, }, - // { - // name: "positive fixint", - // input: []byte{0x64}, - // expected: 100, - // }, - // { - // name: "negative fixint", - // input: []byte{0xEC}, - // expected: -20, - // }, + { + name: "positive fixint", + input: []byte{0x64}, + expected: 100, + }, + { + name: "negative fixint", + input: []byte{0xEC}, + expected: -20, + }, } for _, tc := range testcases { @@ -150,6 +150,7 @@ func TestDeserialize(t *testing.T) { } if !reflect.DeepEqual(result, tc.expected) { t.Errorf("Expected %v, got %v", tc.expected, result) + t.Errorf("Type of expected: %T, Type of got: %T", tc.expected, result) } }) } From e1701b9cfd838b338c92b7c0de5b6432da009ba4 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Sun, 14 Sep 2025 12:35:58 +0300 Subject: [PATCH 12/14] chore: added usage example in main --- cmd/main.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index fe7f767..5f335ac 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,5 +1,22 @@ package main +import ( + "fmt" + + msgpack "github.com/codescalersinternships/msgpack-rawan/pkg" +) + func main() { - + bytes, err := msgpack.Serialize(true) + if err != nil { + panic(err) + } + + deserialized, _, err := msgpack.Deserialize(bytes) + if err != nil { + panic(err) + } + fmt.Printf("Deserialized: %v\n", deserialized) + fmt.Printf("Type: %T\n", deserialized) + } From f44ac602786ce6a65eb5fa71ca6ae4aa8a4e31cb Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Sun, 14 Sep 2025 12:46:00 +0300 Subject: [PATCH 13/14] refactor: deserialization function refactor to hide the returned int --- cmd/main.go | 2 +- pkg/deserializer.go | 17 +++++++++++------ pkg/deserializer_test.go | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 5f335ac..2841538 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,7 +12,7 @@ func main() { panic(err) } - deserialized, _, err := msgpack.Deserialize(bytes) + deserialized, err := msgpack.Deserialize(bytes) if err != nil { panic(err) } diff --git a/pkg/deserializer.go b/pkg/deserializer.go index f631db6..e389bfe 100644 --- a/pkg/deserializer.go +++ b/pkg/deserializer.go @@ -2,7 +2,12 @@ package pkg import "math" -func Deserialize(bytes []byte) (interface{}, int, error) { +func Deserialize(bytes []byte) (interface{}, error) { + result,_,err:= deserialize(bytes) + return result, err +} + +func deserialize(bytes []byte) (interface{}, int, error) { var result interface{} switch bytes[0] { @@ -81,7 +86,7 @@ func Deserialize(bytes []byte) (interface{}, int, error) { arr := make([]any, 0, arrayLen) rest := bytes[1:] for i := 0; i < arrayLen; i++ { - elem, n, _ := Deserialize(rest) + elem, n, _ := deserialize(rest) arr = append(arr, elem) rest = rest[n:] } @@ -93,7 +98,7 @@ func Deserialize(bytes []byte) (interface{}, int, error) { rest := bytes[3:] for i := 0; i < arrayLen; i++ { - elem, n, _ := Deserialize(rest) + elem, n, _ := deserialize(rest) arr = append(arr, elem) rest = rest[n:] } @@ -105,7 +110,7 @@ func Deserialize(bytes []byte) (interface{}, int, error) { rest := bytes[5:] for i := 0; i < arrayLen; i++ { - elem, n, _ := Deserialize(rest) + elem, n, _ := deserialize(rest) arr = append(arr, elem) rest = rest[n:] } @@ -119,11 +124,11 @@ func Deserialize(bytes []byte) (interface{}, int, error) { rest := bytes[1:] consumed := 1 for i := 0; i < mapLen; i++ { - key, n, _ := Deserialize(rest) + key, n, _ := deserialize(rest) consumed += n rest = rest[n:] - val, n, _ := Deserialize(rest) + val, n, _ := deserialize(rest) consumed += n rest = rest[n:] temp_map[key] = val diff --git a/pkg/deserializer_test.go b/pkg/deserializer_test.go index 62696d6..895d517 100644 --- a/pkg/deserializer_test.go +++ b/pkg/deserializer_test.go @@ -144,7 +144,7 @@ func TestDeserialize(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - result, _, err := Deserialize(tc.input) + result, err := Deserialize(tc.input) if err != nil { t.Fatalf("Unexpected error: %v", err) } From e5850e799869aff7b7ee5ff87ec52e880cea93d3 Mon Sep 17 00:00:00 2001 From: RawanMostafa08 Date: Sun, 14 Sep 2025 12:50:13 +0300 Subject: [PATCH 14/14] doc: added user documentatin --- README.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e1a1a5..39a1ce8 100644 --- a/README.md +++ b/README.md @@ -1 +1,41 @@ -# msgpack-rawan \ No newline at end of file +# Msgpack Serializer/Deserializer + +This repository implements the Msgpack object serialization/deserialization + +**Serialization** is conversion from application objects into MessagePack formats via MessagePack type system. + +**Deserialization** is conversion from MessagePack formats into application objects via MessagePack type system. + +## Table of Contents + +- [Installation](#installation) +- [Usage](#usage) + + +## Installation + +1. Clone the repository + + ```bash + git clone https://github.com/codescalersinternships/msgpack-rawan.git + ``` +## APIs +- `Serialize(object interface{}) ([]byte, error)` : Represents the serialization API, given the object itself, it converts it to its serialized bytes + +- `Deserialize(bytes []byte) (interface{}, error)` : Represents the deserialization API, given the serialize bytes, it turns them back to objects + +## Usage + +```go + bytes, err := msgpack.Serialize(true) + if err != nil { + panic(err) + } + + deserialized, err := msgpack.Deserialize(bytes) + if err != nil { + panic(err) + } + fmt.Printf("Deserialized: %v\n", deserialized) + fmt.Printf("Type: %T\n", deserialized) +``` \ No newline at end of file