inetaf / netaddr

Network address types

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

add WriteTo methods

josharian opened this issue · comments

I just came across this Tailscale code:

// writeIPPort writes ipp.String() into sb, with fewer allocations.
//
// TODO: make netaddr more efficient in this area, and retire this func.
func writeIPPort(sb *strbuilder.Builder, ipp netaddr.IPPort) {
	if ipp.IP().Is4() {
		raw := ipp.IP().As4()
		sb.WriteUint(uint64(raw[0]))
		sb.WriteByte('.')
		sb.WriteUint(uint64(raw[1]))
		sb.WriteByte('.')
		sb.WriteUint(uint64(raw[2]))
		sb.WriteByte('.')
		sb.WriteUint(uint64(raw[3]))
		sb.WriteByte(':')
	} else {
		sb.WriteByte('[')
		sb.WriteString(ipp.IP().String()) // TODO: faster?
		sb.WriteString("]:")
	}
	sb.WriteUint(uint64(ipp.Port))
}

We have a similar need in Tailscale's deephash package: We have an IPPort and we want to write it to an io.Writer with minimal allocations. We currently use MarshalText and then copy the result to the io.Writer, which is OK, but is still an unnecessary alloc+copy.

I propose that we add WriteTo methods to IP, IPPort, and IPPrefix. They accept an io.Writer and write the string returned by String() to it. The write may occur as multiple separate calls to Write; they methods do not allocate.

I am not happy about the API proliferation, but this keep keeps coming up as a pain point for me.

Ugh. This doesn't actually result in zero allocations, because any byte slice passed to a Write method escapes. I remember now that that is why we have Append* methods. I'll try that instead...