restic/crypto/writer.go
Alexander Neumann 9c43688d1a crypto: Add buffer management to writer
benchmarks:

    benchmark                         old ns/op     new ns/op     delta
    BenchmarkEncryptWriter            37032949      37754353      +1.95%
    BenchmarkEncrypt                  35989611      36344593      +0.99%
    BenchmarkDecryptReader            38274466      38282979      +0.02%
    BenchmarkEncryptDecryptReader     77506506      77088612      -0.54%
    BenchmarkDecrypt                  36219298      36128875      -0.25%

    benchmark                         old MB/s     new MB/s     speedup
    BenchmarkEncryptWriter            226.52       222.19       0.98x
    BenchmarkEncrypt                  233.08       230.81       0.99x
    BenchmarkDecryptReader            219.17       219.12       1.00x
    BenchmarkEncryptDecryptReader     108.23       108.82       1.01x
    BenchmarkDecrypt                  231.61       232.19       1.00x

    benchmark                         old allocs     new allocs     delta
    BenchmarkEncryptWriter            20             16             -20.00%
    BenchmarkEncrypt                  12             12             +0.00%
    BenchmarkDecryptReader            13             13             +0.00%
    BenchmarkEncryptDecryptReader     32             27             -15.62%
    BenchmarkDecrypt                  10             10             +0.00%

    benchmark                         old bytes     new bytes     delta
    BenchmarkEncryptWriter            1020064       170331        -83.30%
    BenchmarkEncrypt                  1600          1600          +0.00%
    BenchmarkDecryptReader            841070        842094        +0.12%
    BenchmarkEncryptDecryptReader     3027433       845129        -72.08%
    BenchmarkDecrypt                  1573          1573          +0.00%
2015-04-25 00:54:33 +02:00

84 lines
1.6 KiB
Go

package crypto
import (
"crypto/aes"
"crypto/cipher"
"errors"
"fmt"
"io"
)
type encryptWriter struct {
data []byte
key *Key
s cipher.Stream
w io.Writer
closed bool
}
func (e *encryptWriter) Close() error {
if e.closed {
return errors.New("Close() called on already closed writer")
}
e.closed = true
// encrypt everything
iv, c := e.data[:ivSize], e.data[ivSize:]
e.s.XORKeyStream(c, c)
// compute mac
mac := poly1305Sign(c, iv, &e.key.Sign)
e.data = append(e.data, mac...)
// write everything
n, err := e.w.Write(e.data)
if err != nil {
return err
}
if n != len(e.data) {
return errors.New("not all bytes written")
}
// return buffer to pool
freeBuffer(e.data)
return nil
}
func (e *encryptWriter) Write(p []byte) (int, error) {
// if e.data is too small, return it to the buffer and create new slice
if cap(e.data) < len(e.data)+len(p) {
b := make([]byte, len(e.data), len(e.data)*2)
copy(b, e.data)
freeBuffer(e.data)
e.data = b
}
// copy new data to e.data
e.data = append(e.data, p...)
return len(p), nil
}
// EncryptTo buffers data written to the returned io.WriteCloser. When Close()
// is called, the data is encrypted and written to the underlying writer.
func EncryptTo(ks *Key, wr io.Writer) io.WriteCloser {
ew := &encryptWriter{
data: getBuffer(),
key: ks,
}
// buffer iv for mac
ew.data = ew.data[:ivSize]
copy(ew.data, newIV())
c, err := aes.NewCipher(ks.Encrypt[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
ew.s = cipher.NewCTR(c, ew.data[:ivSize])
ew.w = wr
return ew
}