Skip to content

Commit

Permalink
feat: bencoder encoder implementation and testing and documenting
Browse files Browse the repository at this point in the history
Signed-off-by: nabil salah <[email protected]>
  • Loading branch information
Nabil-Salah committed Aug 15, 2024
1 parent a8f25b7 commit 947bb0c
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ test:
go test -v -cover ./...

format:
go fmt ./...
go fmt -w ./...
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ import (
bencoder "github.com/codescalersinternships/bencode-nabil/pkg"
)




func main() {
s := "d3:bar4:spam3:fooi42ee"
s := "d3:bar3:moo4:spaml4:spam4:foooee"

ret,_ := bencoder.Encoder(s)
ret,_ := bencoder.Decoder(s)
fmt.Println(ret)
x,_ := bencoder.Encoder(ret)
fmt.Println(string(x[:]))
}

```
Expand Down
49 changes: 49 additions & 0 deletions pkg/bencoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bencoder

import (
"fmt"
"reflect"
"strconv"
"strings"
)
Expand Down Expand Up @@ -174,3 +175,51 @@ func Decoder(str string, start ...*int) (interface{}, error) {
return out, nil
}

func Encoder(benco interface{} ) ([]byte, error) {

var encoded []byte

switch ty := benco.(type) {
case int, int64:
encoded = append(encoded, 'i')
encoded = append(encoded, []byte(strconv.FormatInt(reflect.ValueOf(ty).Int(), 10))...)
encoded = append(encoded, 'e')

case string:
encoded = append(encoded, []byte(strconv.Itoa(len(ty)))...)
encoded = append(encoded, ':')
encoded = append(encoded, []byte(ty)...)

case []interface{}:
encoded = append(encoded, 'l')
for _, item := range ty {
encodedItem, err := Encoder(item)
if err != nil {
return nil, err
}
encoded = append(encoded, encodedItem...)
}
encoded = append(encoded, 'e')

case map[interface{}]interface{}:
encoded = append(encoded, 'd')
for key, value := range ty {
encodedKey, err := Encoder(key)
if err != nil {
return nil, err
}
encodedValue, err := Encoder(value)
if err != nil {
return nil, err
}
encoded = append(encoded, encodedKey...)
encoded = append(encoded, encodedValue...)
}
encoded = append(encoded, 'e')

default:
return nil, fmt.Errorf("unsupported data type")
}

return encoded, nil
}
106 changes: 94 additions & 12 deletions pkg/bencoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ func TestDecoder(t *testing.T) {
},
{
name: "List of strings",
input: "l4:spam4:eggse",
expected: []interface{}{"spam", "eggs"},
input: "l4:spam4:foooe",
expected: []interface{}{"spam", "fooo"},
expectedError: false,
},
{
name: "Dictionary with strings",
input: "d3:cow3:moo4:spam4:eggse",
input: "d3:bar3:moo4:spam4:foooe",
expected: map[interface {}]interface {}(
map[interface {}]interface {}{
"cow":"moo", "spam":"eggs",
"bar":"moo", "spam":"fooo",
},
),
expectedError: false,
Expand All @@ -50,32 +50,32 @@ func TestDecoder(t *testing.T) {
},
{
name: "Dictionary with strings and integers",
input: "d3:cow3:moo4:spami123ee",
input: "d3:bar3:moo4:spami123ee",
expected: map[interface {}]interface {}(
map[interface {}]interface {}{
"cow":"moo", "spam":int64(123),
"bar":"moo", "spam":int64(123),
},
),
expectedError: false,
},
{
name: "Dictionary with strings and arrays",
input: "d3:cow3:moo4:spaml4:spam4:eggsee",
input: "d3:bar3:moo4:spaml4:spam4:foooee",
expected: map[interface {}]interface {}(
map[interface {}]interface {}{
"cow":"moo",
"spam":[]interface {}{"spam", "eggs"},
"bar":"moo",
"spam":[]interface {}{"spam", "fooo"},
},
),
expectedError: false,
},
{
name: "Dictionary with strings and dictionaries",
input: "d3:cow3:moo4:spamd3:cow3:moo4:spam4:eggsee",
input: "d3:bar3:moo4:spamd3:bar3:moo4:spam4:foooee",
expected: map[interface {}]interface {}(
map[interface {}]interface {}{
"cow":"moo",
"spam":map[interface {}]interface {}{"cow":"moo", "spam":"eggs"},
"bar":"moo",
"spam":map[interface {}]interface {}{"bar":"moo", "spam":"fooo"},
},
),
expectedError: false,
Expand All @@ -91,4 +91,86 @@ func TestDecoder(t *testing.T) {
assert.Equal(t, test.expected, got)
})
}
}

func TestEncoder(t *testing.T) {
tests := []struct {
name string
input interface{}
expected string
expectedError bool
}{
{
name: "Simple string",
input: "spam",
expected: "4:spam",
expectedError: false,
},
{
name: "Integer",
input: int64(123),
expected: "i123e",
expectedError: false,
},
{
name: "List of strings",
input: []interface{}{"spam", "fooo"},
expected: "l4:spam4:foooe",
expectedError: false,
},
{
name: "Dictionary with strings",
input: map[interface{}]interface{}{
"bar": "moo", "spam": "fooo",
},
expected: "d3:bar3:moo4:spam4:foooe",
expectedError: false,
},
{
name: "Unsupported type",
input: 3.14,
expected: "",
expectedError: true,
},
{
name: "Dictionary with strings and integers",
input: map[interface{}]interface{}{
"bar": "moo", "spam": int64(123),
},
expected: "d3:bar3:moo4:spami123ee",
expectedError: false,
},
{
name: "Dictionary with strings and arrays",
input: map[interface{}]interface{}{
"bar": "moo",
"spam": []interface{}{"spam", "fooo"},
},
expected: "d3:bar3:moo4:spaml4:spam4:foooee",
expectedError: false,
},
{
name: "Dictionary with strings and dictionaries",
input: map[interface{}]interface{}{
"bar": "moo",
"spam": map[interface{}]interface{}{
"bar": "moo", "spam": "fooo",
},
},
expected: "d3:bar3:moo4:spamd3:bar3:moo4:spam4:foooee",
expectedError: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got, err := Encoder(test.input)
if test.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, test.expected, string(got))
}
})
}
}

0 comments on commit 947bb0c

Please sign in to comment.