From 872b11bd579ce5c3b49adf2ab873de89fe5a8c25 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Mon, 20 Mar 2023 10:50:22 +1100 Subject: [PATCH] bencode: Support parsing strings into bool --- bencode/decode.go | 17 +++++++++++++++-- bencode/decode_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/bencode/decode.go b/bencode/decode.go index c171221f..6300f5cd 100644 --- a/bencode/decode.go +++ b/bencode/decode.go @@ -10,6 +10,7 @@ import ( "runtime" "strconv" "sync" + "unsafe" ) // The default bencode string length limit. This is a poor attempt to prevent excessive memory @@ -246,13 +247,25 @@ func (d *Decoder) parseString(v reflect.Value) error { if v.Type().Elem().Kind() != reflect.Uint8 { break } - d.buf.Grow(int(length)) + d.buf.Grow(length) b := d.buf.Bytes()[:length] read(b) reflect.Copy(v, reflect.ValueOf(b)) return nil + case reflect.Bool: + d.buf.Grow(length) + b := d.buf.Bytes()[:length] + read(b) + x, err := strconv.ParseBool(unsafe.String(unsafe.SliceData(b), len(b))) + if err != nil { + x = length != 0 + } + v.SetBool(x) + return nil } - d.buf.Grow(int(length)) + // Can't move this into default clause because some cases above fail through to here after + // additional checks. + d.buf.Grow(length) read(d.buf.Bytes()[:length]) // I believe we return here to support "ignore_unmarshal_type_error". return &UnmarshalTypeError{ diff --git a/bencode/decode_test.go b/bencode/decode_test.go index d06da8d3..4d05d2b3 100644 --- a/bencode/decode_test.go +++ b/bencode/decode_test.go @@ -234,3 +234,34 @@ func TestDecodeMaxStrLen(t *testing.T) { Hi []byte `bencode:"420"` }), 69) } + +// This is for the "github.com/anacrolix/torrent/metainfo".Info.Private field. +func TestDecodeStringIntoBoolPtr(t *testing.T) { + var m struct { + Private *bool `bencode:"private,omitempty"` + } + c := qt.New(t) + check := func(msg string, expectNil, expectTrue bool) { + m.Private = nil + c.Check(Unmarshal([]byte(msg), &m), qt.IsNil, qt.Commentf("%q", msg)) + if expectNil { + c.Check(m.Private, qt.IsNil) + } else { + if c.Check(m.Private, qt.IsNotNil, qt.Commentf("%q", msg)) { + c.Check(*m.Private, qt.Equals, expectTrue, qt.Commentf("%q", msg)) + } + } + } + check("d7:privatei1ee", false, true) + check("d7:privatei0ee", false, false) + check("d7:privatei42ee", false, true) + // This is a weird case. We could not allocate the bool to indicate it was bad (maybe a bad + // serializer which isn't uncommon), but that requires reworking the decoder to handle + // automatically. I think if we cared enough we'd create a custom Unmarshaler. Also if we were + // worried enough about performance I'd completely rewrite this package. + check("d7:private0:e", false, false) + check("d7:private1:te", false, true) + check("d7:private5:falsee", false, false) + check("d7:private1:Fe", false, false) + check("d7:private11:bunnyfoofooe", false, true) +} -- 2.44.0