Skip to content

Commit 4ebb58e

Browse files
committed
net,net/netip: implement the encoding.(Binary|Text)Appender
Implement the encoding.TextAppender for "net.IP". Implament the encoding.(Binary|Text)Appender interfaces for "netip.Addr", "netip.AddrPort" and "netip.Prefix". All exported types in the standard library that implement the "encoding.(Binary|Text)Marshaler" now also implement the "encoding.(Binary|Text)Appender". Fixes #62384
1 parent 400e6b6 commit 4ebb58e

File tree

7 files changed

+214
-46
lines changed

7 files changed

+214
-46
lines changed

api/next/62384.txt

+7
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,10 @@ pkg math/rand/v2, method (*ChaCha8) AppendBinary([]uint8) ([]uint8, error) #6238
1515
pkg math/rand/v2, method (*PCG) AppendBinary([]uint8) ([]uint8, error) #62384
1616
pkg crypto/x509, method (OID) AppendBinary([]uint8) ([]uint8, error) #62384
1717
pkg crypto/x509, method (OID) AppendText([]uint8) ([]uint8, error) #62384
18+
pkg net, method (IP) AppendText([]uint8) ([]uint8, error) #62384
19+
pkg net/netip, method (Addr) AppendBinary([]uint8) ([]uint8, error) #62384
20+
pkg net/netip, method (Addr) AppendText([]uint8) ([]uint8, error) #62384
21+
pkg net/netip, method (AddrPort) AppendBinary([]uint8) ([]uint8, error) #62384
22+
pkg net/netip, method (AddrPort) AppendText([]uint8) ([]uint8, error) #62384
23+
pkg net/netip, method (Prefix) AppendBinary([]uint8) ([]uint8, error) #62384
24+
pkg net/netip, method (Prefix) AppendText([]uint8) ([]uint8, error) #62384
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[IP] now implements the [encoding.TextAppender] interface.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[Addr], [AddrPort] and [Prefix] now implement the [encoding.BinaryAppender] and
2+
[encoding.TextAppender] interfaces.

src/net/ip.go

+17-6
Original file line numberDiff line numberDiff line change
@@ -325,17 +325,28 @@ func ipEmptyString(ip IP) string {
325325
return ip.String()
326326
}
327327

328-
// MarshalText implements the [encoding.TextMarshaler] interface.
328+
// AppendText implements the [encoding.TextAppender] interface.
329329
// The encoding is the same as returned by [IP.String], with one exception:
330-
// When len(ip) is zero, it returns an empty slice.
331-
func (ip IP) MarshalText() ([]byte, error) {
330+
// When len(ip) is zero, it does nothing.
331+
func (ip IP) AppendText(b []byte) ([]byte, error) {
332332
if len(ip) == 0 {
333-
return []byte(""), nil
333+
return b, nil
334334
}
335335
if len(ip) != IPv4len && len(ip) != IPv6len {
336-
return nil, &AddrError{Err: "invalid IP address", Addr: hexString(ip)}
336+
return b, &AddrError{Err: "invalid IP address", Addr: hexString(ip)}
337+
}
338+
return append(b, ip.String()...), nil
339+
}
340+
341+
// MarshalText implements the [encoding.TextMarshaler] interface.
342+
// The encoding is the same as returned by [IP.String], with one exception:
343+
// When len(ip) is zero, it returns an empty slice.
344+
func (ip IP) MarshalText() ([]byte, error) {
345+
b, err := ip.AppendText([]byte(""))
346+
if err != nil {
347+
return nil, err
337348
}
338-
return []byte(ip.String()), nil
349+
return b, nil
339350
}
340351

341352
// UnmarshalText implements the [encoding.TextUnmarshaler] interface.

src/net/ip_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ func TestMarshalEmptyIP(t *testing.T) {
149149
if !reflect.DeepEqual(got, []byte("")) {
150150
t.Errorf(`got %#v, want []byte("")`, got)
151151
}
152+
153+
buf := make([]byte, 4)
154+
got, err = ip.AppendText(buf)
155+
if err != nil {
156+
t.Fatal(err)
157+
}
158+
if !reflect.DeepEqual(got, []byte("\x00\x00\x00\x00")) {
159+
t.Errorf(`got %#v, want []byte("\x00\x00\x00\x00")`, got)
160+
}
152161
}
153162

154163
var ipStringTests = []*struct {
@@ -266,6 +275,10 @@ func TestIPString(t *testing.T) {
266275
if out, err := tt.in.MarshalText(); !bytes.Equal(out, tt.byt) || !reflect.DeepEqual(err, tt.error) {
267276
t.Errorf("IP.MarshalText(%v) = %v, %v, want %v, %v", tt.in, out, err, tt.byt, tt.error)
268277
}
278+
buf := make([]byte, 4, 32)
279+
if out, err := tt.in.AppendText(buf); !bytes.Equal(out[4:], tt.byt) || !reflect.DeepEqual(err, tt.error) {
280+
t.Errorf("IP.AppendText(%v) = %v, %v, want %v, %v", tt.in, out[4:], err, tt.byt, tt.error)
281+
}
269282
}
270283
}
271284

src/net/netip/netip.go

+75-40
Original file line numberDiff line numberDiff line change
@@ -966,27 +966,28 @@ func (ip Addr) StringExpanded() string {
966966
return string(ret)
967967
}
968968

969+
// AppendText implements the [encoding.TextAppender] interface,
970+
// It is the same as [Addr.AppendTo].
971+
func (ip Addr) AppendText(b []byte) ([]byte, error) {
972+
return ip.AppendTo(b), nil
973+
}
974+
969975
// MarshalText implements the [encoding.TextMarshaler] interface,
970976
// The encoding is the same as returned by [Addr.String], with one exception:
971977
// If ip is the zero [Addr], the encoding is the empty string.
972978
func (ip Addr) MarshalText() ([]byte, error) {
979+
var maxCap int
973980
switch ip.z {
974981
case z0:
975-
return []byte(""), nil
976982
case z4:
977-
max := len("255.255.255.255")
978-
b := make([]byte, 0, max)
979-
return ip.appendTo4(b), nil
983+
maxCap = len("255.255.255.255")
980984
default:
985+
maxCap = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0")
981986
if ip.Is4In6() {
982-
max := len("::ffff:255.255.255.255%enp5s0")
983-
b := make([]byte, 0, max)
984-
return ip.appendTo4In6(b), nil
987+
maxCap = len("::ffff:255.255.255.255%enp5s0")
985988
}
986-
max := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0")
987-
b := make([]byte, 0, max)
988-
return ip.appendTo6(b), nil
989989
}
990+
return ip.AppendText(make([]byte, 0, maxCap))
990991
}
991992

992993
// UnmarshalText implements the encoding.TextUnmarshaler interface.
@@ -1004,30 +1005,37 @@ func (ip *Addr) UnmarshalText(text []byte) error {
10041005
return err
10051006
}
10061007

1007-
func (ip Addr) marshalBinaryWithTrailingBytes(trailingBytes int) []byte {
1008-
var b []byte
1008+
// AppendBinary implements the [encoding.BinaryAppender] interface.
1009+
func (ip Addr) AppendBinary(b []byte) ([]byte, error) {
10091010
switch ip.z {
10101011
case z0:
1011-
b = make([]byte, trailingBytes)
10121012
case z4:
1013-
b = make([]byte, 4+trailingBytes)
1014-
byteorder.BePutUint32(b, uint32(ip.addr.lo))
1013+
b = byteorder.BeAppendUint32(b, uint32(ip.addr.lo))
10151014
default:
1016-
z := ip.Zone()
1017-
b = make([]byte, 16+len(z)+trailingBytes)
1018-
byteorder.BePutUint64(b[:8], ip.addr.hi)
1019-
byteorder.BePutUint64(b[8:], ip.addr.lo)
1020-
copy(b[16:], z)
1015+
b = byteorder.BeAppendUint64(b, ip.addr.hi)
1016+
b = byteorder.BeAppendUint64(b, ip.addr.lo)
1017+
b = append(b, ip.Zone()...)
1018+
}
1019+
return b, nil
1020+
}
1021+
1022+
func (ip Addr) marshalBinarySize() int {
1023+
switch ip.z {
1024+
case z0:
1025+
return 0
1026+
case z4:
1027+
return 4
1028+
default:
1029+
return 16 + len(ip.Zone())
10211030
}
1022-
return b
10231031
}
10241032

10251033
// MarshalBinary implements the [encoding.BinaryMarshaler] interface.
10261034
// It returns a zero-length slice for the zero [Addr],
10271035
// the 4-byte form for an IPv4 address,
10281036
// and the 16-byte form with zone appended for an IPv6 address.
10291037
func (ip Addr) MarshalBinary() ([]byte, error) {
1030-
return ip.marshalBinaryWithTrailingBytes(0), nil
1038+
return ip.AppendBinary(make([]byte, 0, ip.marshalBinarySize()))
10311039
}
10321040

10331041
// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.
@@ -1198,21 +1206,25 @@ func (p AddrPort) AppendTo(b []byte) []byte {
11981206
return b
11991207
}
12001208

1209+
// AppendText implements the [encoding.TextAppender] interface. The
1210+
// encoding is the same as returned by [AddrPort.AppendTo].
1211+
func (p AddrPort) AppendText(b []byte) ([]byte, error) {
1212+
return p.AppendTo(b), nil
1213+
}
1214+
12011215
// MarshalText implements the [encoding.TextMarshaler] interface. The
12021216
// encoding is the same as returned by [AddrPort.String], with one exception: if
12031217
// p.Addr() is the zero [Addr], the encoding is the empty string.
12041218
func (p AddrPort) MarshalText() ([]byte, error) {
1205-
var max int
1219+
var maxCap int
12061220
switch p.ip.z {
12071221
case z0:
12081222
case z4:
1209-
max = len("255.255.255.255:65535")
1223+
maxCap = len("255.255.255.255:65535")
12101224
default:
1211-
max = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535")
1225+
maxCap = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535")
12121226
}
1213-
b := make([]byte, 0, max)
1214-
b = p.AppendTo(b)
1215-
return b, nil
1227+
return p.AppendText(make([]byte, 0, maxCap))
12161228
}
12171229

12181230
// UnmarshalText implements the encoding.TextUnmarshaler
@@ -1228,13 +1240,22 @@ func (p *AddrPort) UnmarshalText(text []byte) error {
12281240
return err
12291241
}
12301242

1243+
// AppendBinary implements the [encoding.BinaryAppendler] interface.
1244+
// It returns [Addr.AppendBinary] with an additional two bytes appended
1245+
// containing the port in little-endian.
1246+
func (p AddrPort) AppendBinary(b []byte) ([]byte, error) {
1247+
b, err := p.Addr().AppendBinary(b)
1248+
if err != nil {
1249+
return nil, err
1250+
}
1251+
return byteorder.LeAppendUint16(b, p.Port()), nil
1252+
}
1253+
12311254
// MarshalBinary implements the [encoding.BinaryMarshaler] interface.
12321255
// It returns [Addr.MarshalBinary] with an additional two bytes appended
12331256
// containing the port in little-endian.
12341257
func (p AddrPort) MarshalBinary() ([]byte, error) {
1235-
b := p.Addr().marshalBinaryWithTrailingBytes(2)
1236-
byteorder.LePutUint16(b[len(b)-2:], p.Port())
1237-
return b, nil
1258+
return p.AppendBinary(make([]byte, 0, p.Addr().marshalBinarySize()+2))
12381259
}
12391260

12401261
// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.
@@ -1487,21 +1508,25 @@ func (p Prefix) AppendTo(b []byte) []byte {
14871508
return b
14881509
}
14891510

1511+
// AppendText implements the [encoding.TextAppender] interface.
1512+
// It is the same as [Prefix.AppendTo].
1513+
func (p Prefix) AppendText(b []byte) ([]byte, error) {
1514+
return p.AppendTo(b), nil
1515+
}
1516+
14901517
// MarshalText implements the [encoding.TextMarshaler] interface,
14911518
// The encoding is the same as returned by [Prefix.String], with one exception:
14921519
// If p is the zero value, the encoding is the empty string.
14931520
func (p Prefix) MarshalText() ([]byte, error) {
1494-
var max int
1521+
var maxCap int
14951522
switch p.ip.z {
14961523
case z0:
14971524
case z4:
1498-
max = len("255.255.255.255/32")
1525+
maxCap = len("255.255.255.255/32")
14991526
default:
1500-
max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128")
1527+
maxCap = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128")
15011528
}
1502-
b := make([]byte, 0, max)
1503-
b = p.AppendTo(b)
1504-
return b, nil
1529+
return p.AppendText(make([]byte, 0, maxCap))
15051530
}
15061531

15071532
// UnmarshalText implements the encoding.TextUnmarshaler interface.
@@ -1517,13 +1542,23 @@ func (p *Prefix) UnmarshalText(text []byte) error {
15171542
return err
15181543
}
15191544

1545+
// AppendBinary implements the [encoding.AppendMarshaler] interface.
1546+
// It returns [Addr.AppendBinary] with an additional byte appended
1547+
// containing the prefix bits.
1548+
func (p Prefix) AppendBinary(b []byte) ([]byte, error) {
1549+
b, err := p.Addr().withoutZone().AppendBinary(b)
1550+
if err != nil {
1551+
return nil, err
1552+
}
1553+
return append(b, uint8(p.Bits())), nil
1554+
}
1555+
15201556
// MarshalBinary implements the [encoding.BinaryMarshaler] interface.
15211557
// It returns [Addr.MarshalBinary] with an additional byte appended
15221558
// containing the prefix bits.
15231559
func (p Prefix) MarshalBinary() ([]byte, error) {
1524-
b := p.Addr().withoutZone().marshalBinaryWithTrailingBytes(1)
1525-
b[len(b)-1] = uint8(p.Bits())
1526-
return b, nil
1560+
// without the zone the max length is 16, plus an additional byte is 17
1561+
return p.AppendBinary(make([]byte, 0, p.Addr().withoutZone().marshalBinarySize()+1))
15271562
}
15281563

15291564
// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.

0 commit comments

Comments
 (0)