func writeTo(w io.Writer, p []byte) (int, error) {
	n, err := w.Write(p)
	if err != nil {
		return n, err
	}
	if n != len(p) {
		return n, io.ErrShortWrite
	}
	return n, nil
}
writeTo writes p to w. It returns the first error that occurs during
writing, if any. If w violates the io.Writer contract and returns less than
len(p) bytes but no error then writeTo returns io.ErrShortWrite.
package sio
import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/binary"
	"io"
	"math"
	"sync"
	"golang.org/x/crypto/chacha20poly1305"
)
const (
	MaxBufSize = (1 << 24) - 1
	BufSize = 1 << 14
)
const (
	NotAuthentic errorType = "sio: data is not authentic"
	ErrExceeded errorType = "sio: data limit exceeded"
)
type errorType string
func (e errorType) Error() string { return string(e) }
const (
	AES_128_GCM       Algorithm = "AES-128-GCM"         
	AES_256_GCM       Algorithm = "AES-256-GCM"         
	ChaCha20Poly1305  Algorithm = "ChaCha20-Poly1305"   
	XChaCha20Poly1305 Algorithm = "XChaCha20-Poly1305"  
)
type Algorithm string
func (a Algorithm) String() string { return string(a) }
func (a Algorithm) Stream(key []byte) (*Stream, error) { return a.streamWithBufSize(key, BufSize) }
func (a Algorithm) streamWithBufSize(key []byte, bufSize int) (*Stream, error) {
	var (
		aead cipher.AEAD
		err  error
	)
	switch a {
	case AES_128_GCM:
		if len(key) != 128/8 {
			return nil, aes.KeySizeError(len(key))
		}
		aead, err = newAESGCM(key)
	case AES_256_GCM:
		if len(key) != 256/8 {
			return nil, aes.KeySizeError(len(key))
		}
		aead, err = newAESGCM(key)
	case ChaCha20Poly1305:
		aead, err = chacha20poly1305.New(key)
	case XChaCha20Poly1305:
		aead, err = chacha20poly1305.NewX(key)
	default:
		return nil, errorType("sio: invalid algorithm name")
	}
	if err != nil {
		return nil, err
	}
	return NewStream(aead, bufSize), nil
}
func newAESGCM(key []byte) (cipher.AEAD, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	return cipher.NewGCM(block)
}
func NewStream(cipher cipher.AEAD, bufSize int) *Stream {
	if cipher.NonceSize() < 4 {
		panic("sio: NonceSize() of cipher is too small")
	}
	if bufSize > MaxBufSize {
		panic("sio: bufSize is too large")
	}
	if bufSize <= 0 {
		panic("sio: bufSize is too small")
	}
	return &Stream{
		cipher:  cipher,
		bufSize: bufSize,
	}
}
type Stream struct {
	cipher  cipher.AEAD
	bufSize int
}
func (s *Stream) NonceSize() int { return s.cipher.NonceSize() - 4 }
func (s *Stream) Overhead(size int64) int64 {
	if size < 0 {
		return -1
	}
	bufSize := int64(s.bufSize)
	if size > (bufSize * math.MaxUint32) {
		return 0
	}
	overhead := int64(s.cipher.Overhead())
	if size == 0 {
		return overhead
	}
	t := size / bufSize
	if r := size % bufSize; r > 0 {
		return (t * overhead) + overhead
	}
	return t * overhead
}
func (s *Stream) EncryptWriter(w io.Writer, nonce, associatedData []byte) *EncWriter {
	if len(nonce) != s.NonceSize() {
		panic("sio: nonce has invalid length")
	}
	ew := &EncWriter{
		w:              w,
		cipher:         s.cipher,
		bufSize:        s.bufSize,
		nonce:          make([]byte, s.cipher.NonceSize()),
		associatedData: make([]byte, 1+s.cipher.Overhead()),
		buffer:         make([]byte, s.bufSize+s.cipher.Overhead()),
	}
	copy(ew.nonce, nonce)
	nextNonce, _ := ew.nextNonce()
	ew.associatedData[0] = 0x00
	ew.cipher.Seal(ew.associatedData[1:1], nextNonce, nil, associatedData)
	return ew
}
func (s *Stream) DecryptWriter(w io.Writer, nonce, associatedData []byte) *DecWriter {
	if len(nonce) != s.NonceSize() {
		panic("sio: nonce has invalid length")
	}
	dw := &DecWriter{
		w:              w,
		cipher:         s.cipher,
		bufSize:        s.bufSize,
		nonce:          make([]byte, s.cipher.NonceSize()),
		associatedData: make([]byte, 1+s.cipher.Overhead()),
		buffer:         make([]byte, s.bufSize+s.cipher.Overhead(), 1+s.bufSize+s.cipher.Overhead()),
	}
	copy(dw.nonce, nonce)
	nextNonce, _ := dw.nextNonce()
	dw.associatedData[0] = 0x00
	dw.cipher.Seal(dw.associatedData[1:1], nextNonce, nil, associatedData)
	return dw
}
func (s *Stream) EncryptReader(r io.Reader, nonce, associatedData []byte) *EncReader {
	if len(nonce) != s.NonceSize() {
		panic("sio: nonce has invalid length")
	}
	er := &EncReader{
		r:              r,
		cipher:         s.cipher,
		bufSize:        s.bufSize,
		nonce:          make([]byte, s.cipher.NonceSize()),
		associatedData: make([]byte, 1+s.cipher.Overhead()),
		buffer:         make([]byte, 1+s.bufSize+s.cipher.Overhead()),
		firstRead:      true,
	}
	copy(er.nonce, nonce)
	er.associatedData[0] = 0x00
	binary.LittleEndian.PutUint32(er.nonce[er.cipher.NonceSize()-4:], er.seqNum)
	er.cipher.Seal(er.associatedData[1:1], er.nonce, nil, associatedData)
	er.seqNum = 1
	return er
}
func (s *Stream) DecryptReader(r io.Reader, nonce, associatedData []byte) *DecReader {
	if len(nonce) != s.NonceSize() {
		panic("sio: nonce has invalid length")
	}
	dr := &DecReader{
		r:              r,
		cipher:         s.cipher,
		bufSize:        s.bufSize,
		nonce:          make([]byte, s.cipher.NonceSize()),
		associatedData: make([]byte, 1+s.cipher.Overhead()),
		buffer:         make([]byte, 1+s.bufSize+s.cipher.Overhead()),
		firstRead:      true,
	}
	copy(dr.nonce, nonce)
	dr.associatedData[0] = 0x00
	binary.LittleEndian.PutUint32(dr.nonce[dr.cipher.NonceSize()-4:], dr.seqNum)
	dr.cipher.Seal(dr.associatedData[1:1], dr.nonce, nil, associatedData)
	dr.seqNum = 1
	return dr
}
func (s *Stream) DecryptReaderAt(r io.ReaderAt, nonce, associatedData []byte) *DecReaderAt {
	if len(nonce) != s.NonceSize() {
		panic("sio: nonce has invalid length")
	}
	dr := &DecReaderAt{
		r:              r,
		cipher:         s.cipher,
		bufSize:        s.bufSize,
		nonce:          make([]byte, s.cipher.NonceSize()),
		associatedData: make([]byte, 1+s.cipher.Overhead()),
	}
	copy(dr.nonce, nonce)
	dr.associatedData[0] = 0x00
	binary.LittleEndian.PutUint32(dr.nonce[s.NonceSize():], 0)
	dr.cipher.Seal(dr.associatedData[1:1], dr.nonce, nil, associatedData)
	dr.bufPool = sync.Pool{
		New: func() interface{} {
			b := make([]byte, 1+dr.bufSize+dr.cipher.Overhead())
			return &b
		},
	}
	return dr
}