From 527610763b882b56c52d77acf1b75c2f4233c973 Mon Sep 17 00:00:00 2001 From: apocelipes Date: Thu, 15 Aug 2024 18:13:58 +0000 Subject: [PATCH] math/big,regexp: implement the encoding.TextAppender interface For #62384 Change-Id: I1557704c6a0f9c6f3b9aad001374dd5cdbc99065 GitHub-Last-Rev: c258d18ccedab5feeb481a2431d5647bde7e5c58 GitHub-Pull-Request: golang/go#68893 Reviewed-on: https://go-review.googlesource.com/c/go/+/605758 Reviewed-by: Ian Lance Taylor Commit-Queue: Robert Griesemer Reviewed-by: Robert Griesemer Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Auto-Submit: Robert Griesemer --- api/next/62384.txt | 4 ++ doc/next/6-stdlib/99-minor/math/big/62384.md | 1 + doc/next/6-stdlib/99-minor/regexp/62384.md | 1 + src/math/big/floatmarsh.go | 16 +++++--- src/math/big/floatmarsh_test.go | 43 ++++++++++++++++++++ src/math/big/intmarsh.go | 10 +++-- src/math/big/intmarsh_test.go | 33 +++++++++++++++ src/math/big/ratconv.go | 9 ++-- src/math/big/ratmarsh.go | 13 ++++-- src/math/big/ratmarsh_test.go | 23 +++++++++++ src/regexp/all_test.go | 15 +++++++ src/regexp/regexp.go | 12 +++++- 12 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 doc/next/6-stdlib/99-minor/math/big/62384.md create mode 100644 doc/next/6-stdlib/99-minor/regexp/62384.md diff --git a/api/next/62384.txt b/api/next/62384.txt index ece5d9fd80..af7fc5363c 100644 --- a/api/next/62384.txt +++ b/api/next/62384.txt @@ -5,3 +5,7 @@ pkg encoding, type TextAppender interface, AppendText([]uint8) ([]uint8, error) pkg net/url, method (*URL) AppendBinary([]uint8) ([]uint8, error) #62384 pkg log/slog, method (Level) AppendText([]uint8) ([]uint8, error) #62384 pkg log/slog, method (*LevelVar) AppendText([]uint8) ([]uint8, error) #62384 +pkg math/big, method (*Float) AppendText([]uint8) ([]uint8, error) #62384 +pkg math/big, method (*Int) AppendText([]uint8) ([]uint8, error) #62384 +pkg math/big, method (*Rat) AppendText([]uint8) ([]uint8, error) #62384 +pkg regexp, method (*Regexp) AppendText([]uint8) ([]uint8, error) #62384 diff --git a/doc/next/6-stdlib/99-minor/math/big/62384.md b/doc/next/6-stdlib/99-minor/math/big/62384.md new file mode 100644 index 0000000000..4a9418818d --- /dev/null +++ b/doc/next/6-stdlib/99-minor/math/big/62384.md @@ -0,0 +1 @@ +[Float], [Int] and [Rat] now implement the [encoding.TextAppender] interface. diff --git a/doc/next/6-stdlib/99-minor/regexp/62384.md b/doc/next/6-stdlib/99-minor/regexp/62384.md new file mode 100644 index 0000000000..9dcdbad1e8 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/regexp/62384.md @@ -0,0 +1 @@ +[Regexp] now implements the [encoding.TextAppender] interface. diff --git a/src/math/big/floatmarsh.go b/src/math/big/floatmarsh.go index 16be946971..39c22077db 100644 --- a/src/math/big/floatmarsh.go +++ b/src/math/big/floatmarsh.go @@ -106,15 +106,21 @@ func (z *Float) GobDecode(buf []byte) error { return nil } -// MarshalText implements the [encoding.TextMarshaler] interface. +// AppendText implements the [encoding.TextAppender] interface. // Only the [Float] value is marshaled (in full precision), other // attributes such as precision or accuracy are ignored. -func (x *Float) MarshalText() (text []byte, err error) { +func (x *Float) AppendText(b []byte) ([]byte, error) { if x == nil { - return []byte(""), nil + return append(b, ""...), nil } - var buf []byte - return x.Append(buf, 'g', -1), nil + return x.Append(b, 'g', -1), nil +} + +// MarshalText implements the [encoding.TextMarshaler] interface. +// Only the [Float] value is marshaled (in full precision), other +// attributes such as precision or accuracy are ignored. +func (x *Float) MarshalText() (text []byte, err error) { + return x.AppendText(nil) } // UnmarshalText implements the [encoding.TextUnmarshaler] interface. diff --git a/src/math/big/floatmarsh_test.go b/src/math/big/floatmarsh_test.go index 20def68a6d..339cb53764 100644 --- a/src/math/big/floatmarsh_test.go +++ b/src/math/big/floatmarsh_test.go @@ -171,3 +171,46 @@ func TestFloatGobDecodeInvalid(t *testing.T) { } } } + +func TestFloatAppendText(t *testing.T) { + for _, test := range floatVals { + for _, sign := range []string{"", "+", "-"} { + for _, prec := range []uint{0, 1, 2, 10, 53, 64, 100, 1000} { + if prec > 53 && testing.Short() { + continue + } + x := sign + test + var tx Float + _, _, err := tx.SetPrec(prec).Parse(x, 0) + if err != nil { + t.Errorf("parsing of %s (prec = %d) failed (invalid test case): %v", x, prec, err) + continue + } + buf := make([]byte, 4, 32) + b, err := tx.AppendText(buf) + if err != nil { + t.Errorf("marshaling of %v (prec = %d) failed: %v", &tx, prec, err) + continue + } + var rx Float + rx.SetPrec(prec) + if err := rx.UnmarshalText(b[4:]); err != nil { + t.Errorf("unmarshaling of %v (prec = %d) failed: %v", &tx, prec, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("AppendText of %v (prec = %d) failed: got %v want %v", &tx, prec, &rx, &tx) + } + } + } + } +} + +func TestFloatAppendTextNil(t *testing.T) { + var x *Float + buf := make([]byte, 4, 16) + data, _ := x.AppendText(buf) + if string(data[4:]) != "" { + t.Errorf("got %q, want ", data[4:]) + } +} diff --git a/src/math/big/intmarsh.go b/src/math/big/intmarsh.go index 56eeefb884..858ca0faba 100644 --- a/src/math/big/intmarsh.go +++ b/src/math/big/intmarsh.go @@ -45,12 +45,14 @@ func (z *Int) GobDecode(buf []byte) error { return nil } +// AppendText implements the [encoding.TextAppender] interface. +func (x *Int) AppendText(b []byte) (text []byte, err error) { + return x.Append(b, 10), nil +} + // MarshalText implements the [encoding.TextMarshaler] interface. func (x *Int) MarshalText() (text []byte, err error) { - if x == nil { - return []byte(""), nil - } - return x.abs.itoa(x.neg, 10), nil + return x.AppendText(nil) } // UnmarshalText implements the [encoding.TextUnmarshaler] interface. diff --git a/src/math/big/intmarsh_test.go b/src/math/big/intmarsh_test.go index 8e7d29f9dd..681f3dd946 100644 --- a/src/math/big/intmarsh_test.go +++ b/src/math/big/intmarsh_test.go @@ -132,3 +132,36 @@ func TestIntXMLEncoding(t *testing.T) { } } } + +func TestIntAppendText(t *testing.T) { + for _, test := range encodingTests { + for _, sign := range []string{"", "+", "-"} { + x := sign + test + var tx Int + tx.SetString(x, 10) + buf := make([]byte, 4, 32) + b, err := tx.AppendText(buf) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Int + if err := rx.UnmarshalText(b[4:]); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("AppendText of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} + +func TestIntAppendTextNil(t *testing.T) { + var x *Int + buf := make([]byte, 4, 16) + data, _ := x.AppendText(buf) + if string(data[4:]) != "" { + t.Errorf("got %q, want ", data[4:]) + } +} diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go index dd99aecdc0..12f9888c37 100644 --- a/src/math/big/ratconv.go +++ b/src/math/big/ratconv.go @@ -299,12 +299,13 @@ func scanExponent(r io.ByteScanner, base2ok, sepOk bool) (exp int64, base int, e // String returns a string representation of x in the form "a/b" (even if b == 1). func (x *Rat) String() string { - return string(x.marshal()) + return string(x.marshal(nil)) } -// marshal implements String returning a slice of bytes -func (x *Rat) marshal() []byte { - var buf []byte +// marshal implements [Rat.String] returning a slice of bytes. +// It appends the string representation of x in the form "a/b" (even if b == 1) to buf, +// and returns the extended buffer. +func (x *Rat) marshal(buf []byte) []byte { buf = x.a.Append(buf, 10) buf = append(buf, '/') if len(x.b.abs) != 0 { diff --git a/src/math/big/ratmarsh.go b/src/math/big/ratmarsh.go index 6962829453..0457fc9517 100644 --- a/src/math/big/ratmarsh.go +++ b/src/math/big/ratmarsh.go @@ -68,12 +68,17 @@ func (z *Rat) GobDecode(buf []byte) error { return nil } -// MarshalText implements the [encoding.TextMarshaler] interface. -func (x *Rat) MarshalText() (text []byte, err error) { +// AppendText implements the [encoding.TextAppender] interface. +func (x *Rat) AppendText(b []byte) ([]byte, error) { if x.IsInt() { - return x.a.MarshalText() + return x.a.AppendText(b) } - return x.marshal(), nil + return x.marshal(b), nil +} + +// MarshalText implements the [encoding.TextMarshaler] interface. +func (x *Rat) MarshalText() (text []byte, err error) { + return x.AppendText(nil) } // UnmarshalText implements the [encoding.TextUnmarshaler] interface. diff --git a/src/math/big/ratmarsh_test.go b/src/math/big/ratmarsh_test.go index 15c933efa6..7d139bcad8 100644 --- a/src/math/big/ratmarsh_test.go +++ b/src/math/big/ratmarsh_test.go @@ -136,3 +136,26 @@ func TestRatGobDecodeShortBuffer(t *testing.T) { } } } + +func TestRatAppendText(t *testing.T) { + for _, num := range ratNums { + for _, denom := range ratDenoms { + var tx Rat + tx.SetString(num + "/" + denom) + buf := make([]byte, 4, 32) + b, err := tx.AppendText(buf) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Rat + if err := rx.UnmarshalText(b[4:]); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("AppendText of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} diff --git a/src/regexp/all_test.go b/src/regexp/all_test.go index c9c046b61d..ead184d286 100644 --- a/src/regexp/all_test.go +++ b/src/regexp/all_test.go @@ -965,6 +965,21 @@ func TestUnmarshalText(t *testing.T) { if unmarshaled.String() != goodRe[i] { t.Errorf("UnmarshalText returned unexpected value: %s", unmarshaled.String()) } + + buf := make([]byte, 4, 32) + marshalAppend, err := re.AppendText(buf) + if err != nil { + t.Errorf("regexp %#q failed to marshal: %s", re, err) + continue + } + marshalAppend = marshalAppend[4:] + if err := unmarshaled.UnmarshalText(marshalAppend); err != nil { + t.Errorf("regexp %#q failed to unmarshal: %s", re, err) + continue + } + if unmarshaled.String() != goodRe[i] { + t.Errorf("UnmarshalText returned unexpected value: %s", unmarshaled.String()) + } } t.Run("invalid pattern", func(t *testing.T) { re := new(Regexp) diff --git a/src/regexp/regexp.go b/src/regexp/regexp.go index e06099425e..253415fb6a 100644 --- a/src/regexp/regexp.go +++ b/src/regexp/regexp.go @@ -1277,14 +1277,22 @@ func (re *Regexp) Split(s string, n int) []string { return strings } -// MarshalText implements [encoding.TextMarshaler]. The output +// AppendText implements [encoding.TextAppender]. The output // matches that of calling the [Regexp.String] method. // // Note that the output is lossy in some cases: This method does not indicate // POSIX regular expressions (i.e. those compiled by calling [CompilePOSIX]), or // those for which the [Regexp.Longest] method has been called. +func (re *Regexp) AppendText(b []byte) ([]byte, error) { + return append(b, re.String()...), nil +} + +// MarshalText implements [encoding.TextMarshaler]. The output +// matches that of calling the [Regexp.AppendText] method. +// +// See [Regexp.AppendText] for more information. func (re *Regexp) MarshalText() ([]byte, error) { - return []byte(re.String()), nil + return re.AppendText(nil) } // UnmarshalText implements [encoding.TextUnmarshaler] by calling -- 2.48.1