Skip to content

Commit fa1c8bb

Browse files
AnnatarHeclaude
andcommitted
feat: add msgpack encode/decode module
- Add msgpack.go with encode/decode functions for bytes and io interfaces - Add comprehensive tests including benchmarks - Update go.mod to include ugorji/go/codec dependency 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 350a1ab commit fa1c8bb

4 files changed

Lines changed: 225 additions & 0 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ require (
3939
github.com/pmezard/go-difflib v1.0.0 // indirect
4040
github.com/russross/blackfriday/v2 v2.1.0 // indirect
4141
github.com/stretchr/objx v0.5.2 // indirect
42+
github.com/ugorji/go/codec v1.3.0 // indirect
4243
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
4344
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
4445
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
6969
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
7070
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
7171
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
72+
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
73+
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
7274
github.com/uptrace/uptrace-go v1.32.0 h1:j8fmeU5/m0Q4gX1FYlN9GpFrPnpuPUOeK8DVrm1LnBc=
7375
github.com/uptrace/uptrace-go v1.32.0/go.mod h1:ITT13kOzEZKnEqqK7WTEzh9dl7Ilbef/y3EIdv61T7Y=
7476
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=

model/msgpack.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package model
2+
3+
import (
4+
"bytes"
5+
"io"
6+
7+
"github.com/ugorji/go/codec"
8+
)
9+
10+
var msgpackHandle = &codec.MsgpackHandle{
11+
WriteExt: true,
12+
}
13+
14+
func MsgpackEncode(v interface{}) ([]byte, error) {
15+
var buf bytes.Buffer
16+
enc := codec.NewEncoder(&buf, msgpackHandle)
17+
if err := enc.Encode(v); err != nil {
18+
return nil, err
19+
}
20+
return buf.Bytes(), nil
21+
}
22+
23+
func MsgpackDecode(data []byte, v interface{}) error {
24+
dec := codec.NewDecoder(bytes.NewReader(data), msgpackHandle)
25+
return dec.Decode(v)
26+
}
27+
28+
func MsgpackEncodeWriter(w io.Writer, v interface{}) error {
29+
enc := codec.NewEncoder(w, msgpackHandle)
30+
return enc.Encode(v)
31+
}
32+
33+
func MsgpackDecodeReader(r io.Reader, v interface{}) error {
34+
dec := codec.NewDecoder(r, msgpackHandle)
35+
return dec.Decode(v)
36+
}

model/msgpack_test.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package model
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestMsgpackEncodeDecode(t *testing.T) {
12+
t.Run("encode and decode struct", func(t *testing.T) {
13+
type TestStruct struct {
14+
Name string
15+
Age int
16+
Active bool
17+
Tags []string
18+
}
19+
20+
original := TestStruct{
21+
Name: "John Doe",
22+
Age: 30,
23+
Active: true,
24+
Tags: []string{"developer", "golang"},
25+
}
26+
27+
encoded, err := MsgpackEncode(original)
28+
require.NoError(t, err)
29+
assert.NotEmpty(t, encoded)
30+
31+
var decoded TestStruct
32+
err = MsgpackDecode(encoded, &decoded)
33+
require.NoError(t, err)
34+
assert.Equal(t, original, decoded)
35+
})
36+
37+
t.Run("encode and decode map", func(t *testing.T) {
38+
original := map[string]interface{}{
39+
"name": "test",
40+
"value": 123,
41+
"enabled": true,
42+
"items": []int{1, 2, 3},
43+
}
44+
45+
encoded, err := MsgpackEncode(original)
46+
require.NoError(t, err)
47+
assert.NotEmpty(t, encoded)
48+
49+
var decoded map[string]interface{}
50+
err = MsgpackDecode(encoded, &decoded)
51+
require.NoError(t, err)
52+
assert.Equal(t, original["name"], decoded["name"])
53+
assert.Equal(t, original["enabled"], decoded["enabled"])
54+
})
55+
56+
t.Run("encode and decode slice", func(t *testing.T) {
57+
original := []string{"apple", "banana", "cherry"}
58+
59+
encoded, err := MsgpackEncode(original)
60+
require.NoError(t, err)
61+
assert.NotEmpty(t, encoded)
62+
63+
var decoded []string
64+
err = MsgpackDecode(encoded, &decoded)
65+
require.NoError(t, err)
66+
assert.Equal(t, original, decoded)
67+
})
68+
69+
t.Run("encode nil value", func(t *testing.T) {
70+
encoded, err := MsgpackEncode(nil)
71+
require.NoError(t, err)
72+
assert.NotEmpty(t, encoded)
73+
74+
var decoded interface{}
75+
err = MsgpackDecode(encoded, &decoded)
76+
require.NoError(t, err)
77+
assert.Nil(t, decoded)
78+
})
79+
80+
t.Run("decode error with invalid data", func(t *testing.T) {
81+
var decoded string
82+
err := MsgpackDecode([]byte{0xFF, 0xFF, 0xFF}, &decoded)
83+
assert.Error(t, err)
84+
})
85+
}
86+
87+
func TestMsgpackEncodeDecodeWriter(t *testing.T) {
88+
t.Run("encode and decode with writer/reader", func(t *testing.T) {
89+
type TestData struct {
90+
ID int
91+
Message string
92+
}
93+
94+
original := TestData{ID: 42, Message: "hello world"}
95+
96+
var buf bytes.Buffer
97+
err := MsgpackEncodeWriter(&buf, original)
98+
require.NoError(t, err)
99+
100+
var decoded TestData
101+
err = MsgpackDecodeReader(&buf, &decoded)
102+
require.NoError(t, err)
103+
assert.Equal(t, original, decoded)
104+
})
105+
106+
t.Run("multiple encode decode operations", func(t *testing.T) {
107+
var buf bytes.Buffer
108+
109+
// Write multiple values
110+
err := MsgpackEncodeWriter(&buf, "first")
111+
require.NoError(t, err)
112+
113+
err = MsgpackEncodeWriter(&buf, 42)
114+
require.NoError(t, err)
115+
116+
err = MsgpackEncodeWriter(&buf, true)
117+
require.NoError(t, err)
118+
119+
// Read them back
120+
var str string
121+
err = MsgpackDecodeReader(&buf, &str)
122+
require.NoError(t, err)
123+
assert.Equal(t, "first", str)
124+
125+
var num int
126+
err = MsgpackDecodeReader(&buf, &num)
127+
require.NoError(t, err)
128+
assert.Equal(t, 42, num)
129+
130+
var boolean bool
131+
err = MsgpackDecodeReader(&buf, &boolean)
132+
require.NoError(t, err)
133+
assert.True(t, boolean)
134+
})
135+
}
136+
137+
func BenchmarkMsgpackEncode(b *testing.B) {
138+
type BenchData struct {
139+
ID int
140+
Name string
141+
Values []float64
142+
Metadata map[string]string
143+
}
144+
145+
data := BenchData{
146+
ID: 12345,
147+
Name: "benchmark test",
148+
Values: []float64{1.1, 2.2, 3.3, 4.4, 5.5},
149+
Metadata: map[string]string{
150+
"key1": "value1",
151+
"key2": "value2",
152+
},
153+
}
154+
155+
b.ResetTimer()
156+
for i := 0; i < b.N; i++ {
157+
_, _ = MsgpackEncode(data)
158+
}
159+
}
160+
161+
func BenchmarkMsgpackDecode(b *testing.B) {
162+
type BenchData struct {
163+
ID int
164+
Name string
165+
Values []float64
166+
Metadata map[string]string
167+
}
168+
169+
data := BenchData{
170+
ID: 12345,
171+
Name: "benchmark test",
172+
Values: []float64{1.1, 2.2, 3.3, 4.4, 5.5},
173+
Metadata: map[string]string{
174+
"key1": "value1",
175+
"key2": "value2",
176+
},
177+
}
178+
179+
encoded, _ := MsgpackEncode(data)
180+
181+
b.ResetTimer()
182+
for i := 0; i < b.N; i++ {
183+
var decoded BenchData
184+
_ = MsgpackDecode(encoded, &decoded)
185+
}
186+
}

0 commit comments

Comments
 (0)