Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,41 @@
# msgpack-rawan
# 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)
```
22 changes: 22 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +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)

}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/codescalersinternships/msgpack-rawan

go 1.25.0
148 changes: 148 additions & 0 deletions pkg/deserializer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package pkg

import "math"

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] {

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 = int(int8(bytes[0]))
return result, 1, nil
}
}
return result, 0, nil
}
157 changes: 157 additions & 0 deletions pkg/deserializer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
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)
t.Errorf("Type of expected: %T, Type of got: %T", tc.expected, result)
}
})
}
}
Loading