tx_batch.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. package batch
  2. import "net/netip"
  3. const SendBatchCap = 128
  4. // batchWriter is the minimal subset of udp.Conn needed by SendBatch to flush.
  5. type batchWriter interface {
  6. WriteBatch(bufs [][]byte, addrs []netip.AddrPort, outerECNs []byte) error
  7. }
  8. // SendBatch accumulates encrypted UDP packets and flushes them via WriteBatch.
  9. // One SendBatch is owned by each listenIn goroutine; no locking is needed.
  10. // The backing arena grows on demand: when there isn't room for the next slot
  11. // we allocate a fresh backing array. Already-committed slices keep referencing
  12. // the old array and remain valid until Flush drops them.
  13. type SendBatch struct {
  14. out batchWriter
  15. bufs [][]byte
  16. dsts []netip.AddrPort
  17. ecns []byte
  18. backing []byte
  19. }
  20. func NewSendBatch(out batchWriter, batchCap, slotCap int) *SendBatch {
  21. return &SendBatch{
  22. out: out,
  23. bufs: make([][]byte, 0, batchCap),
  24. dsts: make([]netip.AddrPort, 0, batchCap),
  25. ecns: make([]byte, 0, batchCap),
  26. backing: make([]byte, 0, batchCap*slotCap),
  27. }
  28. }
  29. func (b *SendBatch) Reserve(sz int) []byte {
  30. if len(b.backing)+sz > cap(b.backing) {
  31. // Grow: allocate a fresh backing. Already-committed slices still
  32. // reference the old array and remain valid until Flush drops them.
  33. newCap := max(cap(b.backing)*2, sz)
  34. b.backing = make([]byte, 0, newCap)
  35. }
  36. start := len(b.backing)
  37. b.backing = b.backing[:start+sz]
  38. return b.backing[start : start+sz : start+sz]
  39. }
  40. func (b *SendBatch) Commit(pkt []byte, dst netip.AddrPort, outerECN byte) {
  41. b.bufs = append(b.bufs, pkt)
  42. b.dsts = append(b.dsts, dst)
  43. b.ecns = append(b.ecns, outerECN)
  44. }
  45. func (b *SendBatch) Flush() error {
  46. var err error
  47. if len(b.bufs) > 0 {
  48. err = b.out.WriteBatch(b.bufs, b.dsts, b.ecns)
  49. }
  50. for i := range b.bufs {
  51. b.bufs[i] = nil
  52. }
  53. b.bufs = b.bufs[:0]
  54. b.dsts = b.dsts[:0]
  55. b.ecns = b.ecns[:0]
  56. b.backing = b.backing[:0]
  57. return err
  58. }