Files
2025-09-27 04:55:20 +03:00

69 lines
1.7 KiB
Go

package anonymizer
import (
"errors"
"io"
)
const (
chunkSize = 8 * 1024 // 8KB - chunk size for reading from source
overlapSize = 1024 // 1KB - overlap size for pattern matching across chunk boundaries
)
type wrapper struct {
buffer []byte // buffer with processed data ready for output
replacer Replacer // replacer for masking sensitive data
reader io.Reader // source data reader
}
func newWrapper(reader io.Reader, replacer Replacer) io.Reader {
return &wrapper{
buffer: make([]byte, 0, chunkSize),
replacer: replacer,
reader: reader,
}
}
func (w *wrapper) Read(p []byte) (n int, err error) {
// if buffer has enough data for output + preserving overlap area
if len(w.buffer) >= len(p)+overlapSize {
n = copy(p, w.buffer)
// shift buffer left, keeping last overlapSize bytes for next read
// this ensures pattern matching across chunk boundaries
copy(w.buffer, w.buffer[n:])
w.buffer = w.buffer[:len(w.buffer)-n]
return n, nil
}
// read additional data from source
chunk := make([]byte, max(len(p)+overlapSize-len(w.buffer), chunkSize))
for len(w.buffer) < len(p)+overlapSize {
n, err = w.reader.Read(chunk)
if n != 0 {
// append new data to existing buffer
w.buffer = append(w.buffer, chunk[:n]...)
// apply replacer to entire buffer - necessary for pattern matching
// at the junction of old data (from overlap) and new data
w.buffer = w.replacer.ReplaceBytes(w.buffer)
}
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return 0, err
}
}
if len(w.buffer) == 0 {
return 0, io.EOF
}
// return data to user, keeping overlap area in buffer
n = copy(p, w.buffer)
copy(w.buffer, w.buffer[n:])
w.buffer = w.buffer[:len(w.buffer)-n]
return n, nil
}