mirror of
https://github.com/foxcpp/maddy.git
synced 2025-04-06 14:37:37 +03:00
Extract several packages to form a public API
This commit is contained in:
parent
7d497f88f0
commit
bcceec4fe4
170 changed files with 385 additions and 392 deletions
42
framework/buffer/buffer.go
Normal file
42
framework/buffer/buffer.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// The buffer package provides utilities for temporary storage (buffering)
|
||||
// of large blobs.
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Buffer interface represents abstract temporary storage for blobs.
|
||||
//
|
||||
// The Buffer storage is assumed to be immutable. If any modifications
|
||||
// are made - new storage location should be used for them.
|
||||
// This is important to ensure goroutine-safety.
|
||||
//
|
||||
// Since Buffer objects require a careful management of lifetimes, here
|
||||
// is the convention: Its always creator responsibility to call Remove after
|
||||
// Buffer is no longer used. If Buffer object is passed to a function - it is not
|
||||
// guaranteed to be valid after this function returns. If function needs to preserve
|
||||
// the storage contents, it should "re-buffer" it either by reading entire blob
|
||||
// and storing it somewhere or applying implementation-specific methods (for example,
|
||||
// the FileBuffer storage may be "re-buffered" by hard-linking the underlying file).
|
||||
type Buffer interface {
|
||||
// Open creates new Reader reading from the underlying storage.
|
||||
Open() (io.ReadCloser, error)
|
||||
|
||||
// Len reports the length of the stored blob.
|
||||
//
|
||||
// Notably, it indicates the amount of bytes that can be read from the
|
||||
// newly created Reader without hiting io.EOF.
|
||||
Len() int
|
||||
|
||||
// Remove discards buffered body and releases all associated resources.
|
||||
//
|
||||
// Multiple Buffer objects may refer to the same underlying storage.
|
||||
// In this case, care should be taken to ensure that Remove is called
|
||||
// only once since it will discard the shared storage and invalidate
|
||||
// all Buffer objects using it.
|
||||
//
|
||||
// Readers previously created using Open can still be used, but
|
||||
// new ones can't be created.
|
||||
Remove() error
|
||||
}
|
43
framework/buffer/bytesreader.go
Normal file
43
framework/buffer/bytesreader.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// BytesReader is a wrapper for bytes.Reader that stores the original []byte
|
||||
// value and allows to retrieve it.
|
||||
//
|
||||
// It is meant for passing to libraries that expect a io.Reader
|
||||
// but apply certain optimizations when the Reader implements
|
||||
// Bytes() interface.
|
||||
type BytesReader struct {
|
||||
*bytes.Reader
|
||||
value []byte
|
||||
}
|
||||
|
||||
// Bytes returns the unread portion of underlying slice used to construct
|
||||
// BytesReader.
|
||||
func (br BytesReader) Bytes() []byte {
|
||||
return br.value[int(br.Size())-br.Len():]
|
||||
}
|
||||
|
||||
// Copy returns the BytesReader reading from the same slice as br at the same
|
||||
// position.
|
||||
func (br BytesReader) Copy() BytesReader {
|
||||
return NewBytesReader(br.Bytes())
|
||||
}
|
||||
|
||||
// Close is a dummy method for implementation of io.Closer so BytesReader can
|
||||
// be used in MemoryBuffer directly.
|
||||
func (br BytesReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBytesReader(b []byte) BytesReader {
|
||||
// BytesReader and not *BytesReader because BytesReader already wraps two
|
||||
// pointers and double indirection would be pointless.
|
||||
return BytesReader{
|
||||
Reader: bytes.NewReader(b),
|
||||
value: b,
|
||||
}
|
||||
}
|
67
framework/buffer/file.go
Normal file
67
framework/buffer/file.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileBuffer implements Buffer interface using file system.
|
||||
type FileBuffer struct {
|
||||
Path string
|
||||
|
||||
// LenHint is the size of the stored blob. It can
|
||||
// be set to avoid the need to call os.Stat in the
|
||||
// Len() method.
|
||||
LenHint int
|
||||
}
|
||||
|
||||
func (fb FileBuffer) Open() (io.ReadCloser, error) {
|
||||
return os.Open(fb.Path)
|
||||
}
|
||||
|
||||
func (fb FileBuffer) Len() int {
|
||||
if fb.LenHint != 0 {
|
||||
return fb.LenHint
|
||||
}
|
||||
|
||||
info, err := os.Stat(fb.Path)
|
||||
if err != nil {
|
||||
// Any access to the file will probably fail too. So we can't return a
|
||||
// sensible value.
|
||||
return 0
|
||||
}
|
||||
|
||||
return int(info.Size())
|
||||
}
|
||||
|
||||
func (fb FileBuffer) Remove() error {
|
||||
return os.Remove(fb.Path)
|
||||
}
|
||||
|
||||
// BufferInFile is a convenience function which creates FileBuffer with underlying
|
||||
// file created in the specified directory with the random name.
|
||||
func BufferInFile(r io.Reader, dir string) (Buffer, error) {
|
||||
// It is assumed that PRNG is initialized somewhere during program startup.
|
||||
nameBytes := make([]byte, 32)
|
||||
_, err := rand.Read(nameBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("buffer: failed to generate randomness for file name: %v", err)
|
||||
}
|
||||
path := filepath.Join(dir, hex.EncodeToString(nameBytes))
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("buffer: failed to create file: %v", err)
|
||||
}
|
||||
if _, err = io.Copy(f, r); err != nil {
|
||||
return nil, fmt.Errorf("buffer: failed to write file: %v", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, fmt.Errorf("buffer: failed to close file: %v", err)
|
||||
}
|
||||
|
||||
return FileBuffer{Path: path}, nil
|
||||
}
|
33
framework/buffer/memory.go
Normal file
33
framework/buffer/memory.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// MemoryBuffer implements Buffer interface using byte slice.
|
||||
type MemoryBuffer struct {
|
||||
Slice []byte
|
||||
}
|
||||
|
||||
func (mb MemoryBuffer) Open() (io.ReadCloser, error) {
|
||||
return NewBytesReader(mb.Slice), nil
|
||||
}
|
||||
|
||||
func (mb MemoryBuffer) Len() int {
|
||||
return len(mb.Slice)
|
||||
}
|
||||
|
||||
func (mb MemoryBuffer) Remove() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BufferInMemory is a convenience function which creates MemoryBuffer with
|
||||
// contents of the passed io.Reader.
|
||||
func BufferInMemory(r io.Reader) (Buffer, error) {
|
||||
blob, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return MemoryBuffer{Slice: blob}, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue