// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

// Package batching implements a socket optimized for increased throughput.
package batching

import (
	"net/netip"

	"github.com/metacubex/tailscale/net/packet"
	"github.com/metacubex/tailscale/types/nettype"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

var (
	// This acts as a compile-time check for our usage of ipv6.Message in
	// [Conn] for both IPv6 and IPv4 operations.
	_ ipv6.Message = ipv4.Message{}
)

// Conn is a [nettype.PacketConn] that provides batched i/o using
// platform-specific optimizations, e.g. {recv,send}mmsg & UDP GSO/GRO.
//
// Conn does not support single packet reads (see ReadFromUDPAddrPort docs). It
// is the caller's responsibility to use the appropriate read API where a
// [nettype.PacketConn] has been upgraded to support batched i/o.
//
// Conn originated from (and is still used by) magicsock where its API was
// strongly influenced by [wireguard-go/conn.Bind] constraints, namely
// wireguard-go's ownership of packet memory.
type Conn interface {
	nettype.PacketConn
	// ReadFromUDPAddrPort always returns an error, as UDP GRO is incompatible
	// with single packet reads. A single datagram may be multiple, coalesced
	// datagrams, and this API lacks the ability to pass that context.
	//
	// TODO: consider detaching Conn from [nettype.PacketConn]
	ReadFromUDPAddrPort([]byte) (int, netip.AddrPort, error)
	// ReadBatch reads messages from [Conn] into msgs. It returns the number of
	// messages the caller should evaluate for nonzero len, as a zero len
	// message may fall on either side of a nonzero.
	//
	// Each [ipv6.Message.OOB] must be sized to at least MinControlMessageSize().
	ReadBatch(msgs []ipv6.Message, flags int) (n int, err error)
	// WriteBatchTo writes buffs to addr.
	//
	// If geneve.VNI.IsSet(), then geneve is encoded into the space preceding
	// offset, and offset must equal [packet.GeneveFixedHeaderLength]. If
	// !geneve.VNI.IsSet() then the space preceding offset is ignored.
	//
	// len(buffs) must be <= batchSize supplied in TryUpgradeToConn().
	//
	// WriteBatchTo may return a [neterror.ErrUDPGSODisabled] error if UDP GSO
	// was disabled as a result of a send error.
	WriteBatchTo(buffs [][]byte, addr netip.AddrPort, geneve packet.GeneveHeader, offset int) error
}
