| // 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" |
| "sync" |
| ) |
| |
| 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 |
| } |
| |
| var defaultMmsghdrsPool = mmsghdrsPool{ |
| p: sync.Pool{ |
| New: func() interface{} { |
| return new(mmsghdrsPacker) |
| }, |
| }, |
| } |
| |
| type mmsghdrsPool struct { |
| p sync.Pool |
| } |
| |
| func (p *mmsghdrsPool) Get() *mmsghdrsPacker { |
| return p.p.Get().(*mmsghdrsPacker) |
| } |
| |
| func (p *mmsghdrsPool) Put(packer *mmsghdrsPacker) { |
| p.p.Put(packer) |
| } |