// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build aix || linux || netbsd // +build aix linux netbsd package socket import ( "net" "os" "sync" "syscall" ) type mmsghdrs []mmsghdr func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error { for i := range hs { ms[i].N = int(hs[i].Len) ms[i].NN = hs[i].Hdr.controllen() ms[i].Flags = hs[i].Hdr.flags() if parseFn != nil { var err error ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint) if err != nil { return err } } } return nil } // mmsghdrsPacker packs Message-slices into mmsghdrs (re-)using pre-allocated buffers. type mmsghdrsPacker struct { // hs are the pre-allocated mmsghdrs. hs mmsghdrs // sockaddrs is the pre-allocated buffer for the Hdr.Name buffers. // We use one large buffer for all messages and slice it up. sockaddrs []byte // vs are the pre-allocated iovecs. // We allocate one large buffer for all messages and slice it up. This allows to reuse the buffer // if the number of buffers per message is distributed differently between calls. vs []iovec } func (p *mmsghdrsPacker) prepare(ms []Message) { n := len(ms) if n <= cap(p.hs) { p.hs = p.hs[:n] } else { p.hs = make(mmsghdrs, n) } if n*sizeofSockaddrInet6 <= cap(p.sockaddrs) { p.sockaddrs = p.sockaddrs[:n*sizeofSockaddrInet6] } else { p.sockaddrs = make([]byte, n*sizeofSockaddrInet6) } nb := 0 for _, m := range ms { nb += len(m.Buffers) } if nb <= cap(p.vs) { p.vs = p.vs[:nb] } else { p.vs = make([]iovec, nb) } } func (p *mmsghdrsPacker) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr, []byte) int) mmsghdrs { p.prepare(ms) hs := p.hs vsRest := p.vs saRest := p.sockaddrs for i := range hs { nvs := len(ms[i].Buffers) vs := vsRest[:nvs] vsRest = vsRest[nvs:] var sa []byte if parseFn != nil { sa = saRest[:sizeofSockaddrInet6] saRest = saRest[sizeofSockaddrInet6:] } else if marshalFn != nil { n := marshalFn(ms[i].Addr, saRest) if n > 0 { sa = saRest[:n] saRest = saRest[n:] } } hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa) } return hs } // syscaller is a helper to invoke recvmmsg and sendmmsg via the RawConn.Read/Write interface. // It is reusable, to amortize the overhead of allocating a closure for the function passed to // RawConn.Read/Write. type syscaller struct { n int operr error hs mmsghdrs flags int boundRecvmmsgF func(uintptr) bool boundSendmmsgF func(uintptr) bool } func (r *syscaller) init() { r.boundRecvmmsgF = r.recvmmsgF r.boundSendmmsgF = r.sendmmsgF } func (r *syscaller) recvmmsg(c syscall.RawConn, hs mmsghdrs, flags int) (int, error) { r.n = 0 r.operr = nil r.hs = hs r.flags = flags if err := c.Read(r.boundRecvmmsgF); err != nil { return r.n, err } if r.operr != nil { return r.n, os.NewSyscallError("recvmmsg", r.operr) } return r.n, nil } func (r *syscaller) recvmmsgF(s uintptr) bool { r.n, r.operr = recvmmsg(s, r.hs, r.flags) return ioComplete(r.flags, r.operr) } func (r *syscaller) sendmmsg(c syscall.RawConn, hs mmsghdrs, flags int) (int, error) { r.n = 0 r.operr = nil r.hs = hs r.flags = flags if err := c.Write(r.boundSendmmsgF); err != nil { return r.n, err } if r.operr != nil { return r.n, os.NewSyscallError("sendmmsg", r.operr) } return r.n, nil } func (r *syscaller) sendmmsgF(s uintptr) bool { r.n, r.operr = sendmmsg(s, r.hs, r.flags) return ioComplete(r.flags, r.operr) } // mmsgTmps holds reusable temporary helpers for recvmmsg and sendmmsg. type mmsgTmps struct { packer mmsghdrsPacker syscaller syscaller } var defaultMmsgTmpsPool = mmsgTmpsPool{ p: sync.Pool{ New: func() interface{} { tmps := new(mmsgTmps) tmps.syscaller.init() return tmps }, }, } type mmsgTmpsPool struct { p sync.Pool } func (p *mmsgTmpsPool) Get() *mmsgTmps { m := p.p.Get().(*mmsgTmps) // Clear fields up to the len (not the cap) of the slice, // assuming that the previous caller only used that many elements. for i := range m.packer.sockaddrs { m.packer.sockaddrs[i] = 0 } m.packer.sockaddrs = m.packer.sockaddrs[:0] for i := range m.packer.vs { m.packer.vs[i] = iovec{} } m.packer.vs = m.packer.vs[:0] for i := range m.packer.hs { m.packer.hs[i].Len = 0 m.packer.hs[i].Hdr = msghdr{} } m.packer.hs = m.packer.hs[:0] return m } func (p *mmsgTmpsPool) Put(tmps *mmsgTmps) { p.p.Put(tmps) }