From 77697f325020b662bebb218ac23010d268485bd1 Mon Sep 17 00:00:00 2001 From: jet2tlf Date: Mon, 3 Jun 2024 14:22:45 -0300 Subject: codecrafters submit [skip ci] --- cmd/mybittorrent/decoder.go | 98 +++++++++++++++++++++++++++++++++++++++++++++ cmd/mybittorrent/main.go | 2 +- codecrafters.yml | 2 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 cmd/mybittorrent/decoder.go diff --git a/cmd/mybittorrent/decoder.go b/cmd/mybittorrent/decoder.go new file mode 100644 index 0000000..10066e1 --- /dev/null +++ b/cmd/mybittorrent/decoder.go @@ -0,0 +1,98 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +type Decoder struct { + raw string + decoderFuncs []DecoderFunc +} + +type DecoderFunc func(string) (any, int, bool) + +func NewDecoder(raw string) *Decoder { + d := &Decoder{raw: raw} + return d.WithDecoderFunc(d.decodeSlice).WithDecoderFunc(d.decodeString).WithDecoderFunc(d.decodeInteger) +} + +func (d *Decoder) WithDecoderFunc(f DecoderFunc) *Decoder { + d.decoderFuncs = append(d.decoderFuncs, f) + return d +} + +func (d *Decoder) Decode() (any, error) { + for _, f := range d.decoderFuncs { + if r, _, ok := f(d.raw); ok { + return r, nil + } + } + + return nil, fmt.Errorf("failed to decode value: %s", d.raw) +} + +func (d *Decoder) decodeString(chunk string) (any, int, bool) { + if !unicode.IsDigit(rune(chunk[0])) { + return "", 0, false + } + + var firstColonIndex int + + for i := 0; i < len(chunk); i++ { + if chunk[i] == ':' { + firstColonIndex = i + break + } + } + + lengthStr := chunk[:firstColonIndex] + + length, err := strconv.Atoi(lengthStr) + if err != nil { + return "", 0, false + } + + val := chunk[firstColonIndex+1 : firstColonIndex+1+length] + + return val, length + 1 + len(lengthStr), true +} + +func (d *Decoder) decodeInteger(chunk string) (any, int, bool) { + if rune(chunk[0]) != 'i' { + return 0, 0, false + } + + num, err := strconv.Atoi(chunk[1:strings.IndexByte(chunk, 'e')]) + if err != nil { + return 0, 0, false + } + + return num, len(strconv.Itoa(num)) + 2, true +} + +func (d *Decoder) decodeSlice(chunk string) (any, int, bool) { + if rune(chunk[0]) != 'l' { + return nil, 0, false + } + + if rune(chunk[1]) == 'e' { + return []any{}, 2, true + } + + slice := make([]any, 0, 1024) + offset := 1 + + for rune(chunk[offset]) != 'e' { + for _, f := range d.decoderFuncs { + if v, l, ok := f(chunk[offset:]); ok { + slice = append(slice, v) + offset += l + } + } + } + + return slice, offset + 1, true +} diff --git a/cmd/mybittorrent/main.go b/cmd/mybittorrent/main.go index 87095ae..e3e902a 100644 --- a/cmd/mybittorrent/main.go +++ b/cmd/mybittorrent/main.go @@ -42,7 +42,7 @@ func main() { if command == "decode" { bencodedValue := os.Args[2] - decoded, err := decodeBencode(bencodedValue) + decoded, err := NewDecoder(bencodedValue).Decode() if err != nil { fmt.Println(err) return diff --git a/codecrafters.yml b/codecrafters.yml index 77886cf..7f21f4d 100644 --- a/codecrafters.yml +++ b/codecrafters.yml @@ -2,7 +2,7 @@ # # These can be VERY verbose, so we suggest turning them off # unless you really need them. -debug: false +debug: true # Use this to change the Go version used to run your code # on Codecrafters. -- cgit v1.2.3