118 lines
2.4 KiB
Go
118 lines
2.4 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/allegro/bigcache/v3"
|
|
"github.com/pkg/errors"
|
|
"gitlab.com/wpetit/goweb/logger"
|
|
)
|
|
|
|
type readCacher struct {
|
|
reader io.ReadSeekCloser
|
|
cache *bigcache.BigCache
|
|
key string
|
|
}
|
|
|
|
// Close implements io.ReadSeekCloser.
|
|
func (r *readCacher) Close() error {
|
|
if err := r.reader.Close(); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Read implements io.ReadSeekCloser.
|
|
func (r *readCacher) Read(p []byte) (n int, err error) {
|
|
length, err := r.reader.Read(p)
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return length, io.EOF
|
|
}
|
|
|
|
return length, errors.WithStack(err)
|
|
}
|
|
|
|
if length > 0 {
|
|
if err := r.cache.Append(r.key, p[:length]); err != nil {
|
|
ctx := logger.With(context.Background(), logger.F("cacheKey", r.key))
|
|
logger.Error(ctx, "could not write to buffer", logger.CapturedE(errors.WithStack(err)))
|
|
|
|
if err := r.cache.Delete(r.key); err != nil {
|
|
logger.Error(ctx, "could not delete cache key", logger.CapturedE(errors.WithStack(err)))
|
|
}
|
|
}
|
|
}
|
|
|
|
return length, nil
|
|
}
|
|
|
|
// Seek implements io.ReadSeekCloser.
|
|
func (r *readCacher) Seek(offset int64, whence int) (int64, error) {
|
|
length, err := r.reader.Seek(offset, whence)
|
|
if err != nil {
|
|
return length, errors.WithStack(err)
|
|
}
|
|
|
|
return length, nil
|
|
}
|
|
|
|
var _ io.ReadSeekCloser = &readCacher{}
|
|
|
|
type cachedReader struct {
|
|
buffer []byte
|
|
offset int64
|
|
}
|
|
|
|
// Read implements io.ReadSeekCloser.
|
|
func (r *cachedReader) Read(p []byte) (n int, err error) {
|
|
available := len(r.buffer) - int(r.offset)
|
|
if available == 0 {
|
|
return 0, io.EOF
|
|
}
|
|
|
|
size := len(p)
|
|
if size > available {
|
|
size = available
|
|
}
|
|
|
|
copy(p, r.buffer[r.offset:r.offset+int64(size)])
|
|
r.offset += int64(size)
|
|
|
|
return size, nil
|
|
}
|
|
|
|
// Close implements io.ReadSeekCloser.
|
|
func (r *cachedReader) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// Seek implements io.ReadSeekCloser.
|
|
func (r *cachedReader) Seek(offset int64, whence int) (int64, error) {
|
|
var newOffset int64
|
|
|
|
switch whence {
|
|
case io.SeekStart:
|
|
newOffset = offset
|
|
case io.SeekCurrent:
|
|
newOffset = r.offset + offset
|
|
case io.SeekEnd:
|
|
newOffset = int64(len(r.buffer)) + offset
|
|
default:
|
|
return 0, errors.Errorf("unknown seek whence '%d'", whence)
|
|
}
|
|
|
|
if newOffset > int64(len(r.buffer)) || newOffset < 0 {
|
|
return 0, fmt.Errorf("invalid offset %d", offset)
|
|
}
|
|
|
|
r.offset = newOffset
|
|
|
|
return newOffset, nil
|
|
}
|
|
|
|
var _ io.ReadSeekCloser = &cachedReader{}
|