diff options
author | 2022-04-26 20:30:25 -0700 | |
---|---|---|
committer | 2023-01-31 15:16:42 +0100 | |
commit | 83b4c9ebc87d0fddf4e638f13e3af1483912e3a5 (patch) | |
tree | 47840b84c0fd3cb226eab2ecb3dbce0617163406 /vendor/codeberg.org/gruf/go-store/storage | |
parent | [chore] update URLs to forked source (diff) | |
download | gotosocial-83b4c9ebc87d0fddf4e638f13e3af1483912e3a5.tar.xz |
[chore] remove vendor
Diffstat (limited to 'vendor/codeberg.org/gruf/go-store/storage')
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/block.go | 895 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/compressor.go | 104 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/disk.go | 400 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/errors.go | 90 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/fs.go | 65 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/lock.go | 76 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/memory.go | 188 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/storage.go | 54 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-store/storage/transform.go | 25 |
9 files changed, 0 insertions, 1897 deletions
diff --git a/vendor/codeberg.org/gruf/go-store/storage/block.go b/vendor/codeberg.org/gruf/go-store/storage/block.go deleted file mode 100644 index c0bb6b383..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/block.go +++ /dev/null @@ -1,895 +0,0 @@ -package storage - -import ( - "bytes" - "crypto/sha256" - "fmt" - "io" - "io/fs" - "os" - "strings" - "sync" - "syscall" - - "codeberg.org/gruf/go-byteutil" - "codeberg.org/gruf/go-errors/v2" - "codeberg.org/gruf/go-fastcopy" - "codeberg.org/gruf/go-hashenc" - "codeberg.org/gruf/go-pools" - "codeberg.org/gruf/go-store/util" -) - -var ( - nodePathPrefix = "node/" - blockPathPrefix = "block/" -) - -// DefaultBlockConfig is the default BlockStorage configuration -var DefaultBlockConfig = &BlockConfig{ - BlockSize: 1024 * 16, - WriteBufSize: 4096, - Overwrite: false, - Compression: NoCompression(), -} - -// BlockConfig defines options to be used when opening a BlockStorage -type BlockConfig struct { - // BlockSize is the chunking size to use when splitting and storing blocks of data - BlockSize int - - // ReadBufSize is the buffer size to use when reading node files - ReadBufSize int - - // WriteBufSize is the buffer size to use when writing file streams (PutStream) - WriteBufSize int - - // Overwrite allows overwriting values of stored keys in the storage - Overwrite bool - - // Compression is the Compressor to use when reading / writing files, default is no compression - Compression Compressor -} - -// getBlockConfig returns a valid BlockConfig for supplied ptr -func getBlockConfig(cfg *BlockConfig) BlockConfig { - // If nil, use default - if cfg == nil { - cfg = DefaultBlockConfig - } - - // Assume nil compress == none - if cfg.Compression == nil { - cfg.Compression = NoCompression() - } - - // Assume 0 chunk size == use default - if cfg.BlockSize < 1 { - cfg.BlockSize = DefaultBlockConfig.BlockSize - } - - // Assume 0 buf size == use default - if cfg.WriteBufSize < 1 { - cfg.WriteBufSize = DefaultDiskConfig.WriteBufSize - } - - // Return owned config copy - return BlockConfig{ - BlockSize: cfg.BlockSize, - WriteBufSize: cfg.WriteBufSize, - Overwrite: cfg.Overwrite, - Compression: cfg.Compression, - } -} - -// BlockStorage is a Storage implementation that stores input data as chunks on -// a filesystem. Each value is chunked into blocks of configured size and these -// blocks are stored with name equal to their base64-encoded SHA256 hash-sum. A -// "node" file is finally created containing an array of hashes contained within -// this value -type BlockStorage struct { - path string // path is the root path of this store - blockPath string // blockPath is the joined root path + block path prefix - nodePath string // nodePath is the joined root path + node path prefix - config BlockConfig // cfg is the supplied configuration for this store - hashPool sync.Pool // hashPool is this store's hashEncoder pool - bufpool pools.BufferPool // bufpool is this store's bytes.Buffer pool - cppool fastcopy.CopyPool // cppool is the prepared io copier with buffer pool - lock *Lock // lock is the opened lockfile for this storage instance - - // NOTE: - // BlockStorage does not need to lock each of the underlying block files - // as the filename itself directly relates to the contents. If there happens - // to be an overwrite, it will just be of the same data since the filename is - // the hash of the data. -} - -// OpenBlock opens a BlockStorage instance for given folder path and configuration -func OpenBlock(path string, cfg *BlockConfig) (*BlockStorage, error) { - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - // Clean provided path, ensure ends in '/' (should - // be dir, this helps with file path trimming later) - path = pb.Clean(path) + "/" - - // Get checked config - config := getBlockConfig(cfg) - - // Attempt to open path - file, err := os.OpenFile(path, defaultFileROFlags, defaultDirPerms) - if err != nil { - // If not a not-exist error, return - if !os.IsNotExist(err) { - return nil, err - } - - // Attempt to make store path dirs - err = os.MkdirAll(path, defaultDirPerms) - if err != nil { - return nil, err - } - - // Reopen dir now it's been created - file, err = os.OpenFile(path, defaultFileROFlags, defaultDirPerms) - if err != nil { - return nil, err - } - } - defer file.Close() - - // Double check this is a dir (NOT a file!) - stat, err := file.Stat() - if err != nil { - return nil, err - } else if !stat.IsDir() { - return nil, errPathIsFile - } - - // Open and acquire storage lock for path - lock, err := OpenLock(pb.Join(path, LockFile)) - if err != nil { - return nil, err - } - - // Figure out the largest size for bufpool slices - bufSz := encodedHashLen - if bufSz < config.BlockSize { - bufSz = config.BlockSize - } - if bufSz < config.WriteBufSize { - bufSz = config.WriteBufSize - } - - // Prepare BlockStorage - st := &BlockStorage{ - path: path, - blockPath: pb.Join(path, blockPathPrefix), - nodePath: pb.Join(path, nodePathPrefix), - config: config, - hashPool: sync.Pool{ - New: func() interface{} { - return newHashEncoder() - }, - }, - bufpool: pools.NewBufferPool(bufSz), - lock: lock, - } - - // Set copypool buffer size - st.cppool.Buffer(config.ReadBufSize) - - return st, nil -} - -// Clean implements storage.Clean() -func (st *BlockStorage) Clean() error { - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - nodes := map[string]*node{} - onceErr := errors.OnceError{} - - // Walk nodes dir for entries - err := util.WalkDir(pb, st.nodePath, func(npath string, fsentry fs.DirEntry) { - // Only deal with regular files - if !fsentry.Type().IsRegular() { - return - } - - // Stop if we hit error previously - if onceErr.IsSet() { - return - } - - // Get joined node path name - npath = pb.Join(npath, fsentry.Name()) - - // Attempt to open RO file - file, err := open(npath, defaultFileROFlags) - if err != nil { - onceErr.Store(err) - return - } - defer file.Close() - - // Alloc new Node + acquire hash buffer for writes - hbuf := st.bufpool.Get() - defer st.bufpool.Put(hbuf) - hbuf.Guarantee(encodedHashLen) - node := node{} - - // Write file contents to node - _, err = io.CopyBuffer( - &nodeWriter{ - node: &node, - buf: hbuf, - }, - file, - nil, - ) - if err != nil { - onceErr.Store(err) - return - } - - // Append to nodes slice - nodes[fsentry.Name()] = &node - }) - - // Handle errors (though nodePath may not have been created yet) - if err != nil && !os.IsNotExist(err) { - return err - } else if onceErr.IsSet() { - return onceErr.Load() - } - - // Walk blocks dir for entries - onceErr.Reset() - err = util.WalkDir(pb, st.blockPath, func(bpath string, fsentry fs.DirEntry) { - // Only deal with regular files - if !fsentry.Type().IsRegular() { - return - } - - // Stop if we hit error previously - if onceErr.IsSet() { - return - } - - inUse := false - for key, node := range nodes { - if node.removeHash(fsentry.Name()) { - if len(node.hashes) < 1 { - // This node contained hash, and after removal is now empty. - // Remove this node from our tracked nodes slice - delete(nodes, key) - } - inUse = true - } - } - - // Block hash is used by node - if inUse { - return - } - - // Get joined block path name - bpath = pb.Join(bpath, fsentry.Name()) - - // Remove this unused block path - err := os.Remove(bpath) - if err != nil { - onceErr.Store(err) - return - } - }) - - // Handle errors (though blockPath may not have been created yet) - if err != nil && !os.IsNotExist(err) { - return err - } else if onceErr.IsSet() { - return onceErr.Load() - } - - // If there are nodes left at this point, they are corrupt - // (i.e. they're referencing block hashes that don't exist) - if len(nodes) > 0 { - nodeKeys := []string{} - for key := range nodes { - nodeKeys = append(nodeKeys, key) - } - return fmt.Errorf("store/storage: corrupted nodes: %v", nodeKeys) - } - - return nil -} - -// ReadBytes implements Storage.ReadBytes() -func (st *BlockStorage) ReadBytes(key string) ([]byte, error) { - // Get stream reader for key - rc, err := st.ReadStream(key) - if err != nil { - return nil, err - } - defer rc.Close() - - // Read all bytes and return - return io.ReadAll(rc) -} - -// ReadStream implements Storage.ReadStream() -func (st *BlockStorage) ReadStream(key string) (io.ReadCloser, error) { - // Get node file path for key - npath, err := st.nodePathForKey(key) - if err != nil { - return nil, err - } - - // Track open - st.lock.Add() - - // Check if open - if st.lock.Closed() { - st.lock.Done() - return nil, ErrClosed - } - - // Attempt to open RO file - file, err := open(npath, defaultFileROFlags) - if err != nil { - st.lock.Done() - return nil, errSwapNotFound(err) - } - defer file.Close() - - // Acquire hash buffer for writes - hbuf := st.bufpool.Get() - defer st.bufpool.Put(hbuf) - - // Write file contents to node - node := node{} - _, err = st.cppool.Copy( - &nodeWriter{ - node: &node, - buf: hbuf, - }, - file, - ) - if err != nil { - st.lock.Done() - return nil, err - } - - // Prepare block reader and return - rc := util.NopReadCloser(&blockReader{ - storage: st, - node: &node, - }) // we wrap the blockreader to decr lockfile waitgroup - return util.ReadCloserWithCallback(rc, st.lock.Done), nil -} - -func (st *BlockStorage) readBlock(key string) ([]byte, error) { - // Get block file path for key - bpath := st.blockPathForKey(key) - - // Attempt to open RO file - file, err := open(bpath, defaultFileROFlags) - if err != nil { - return nil, wrap(errCorruptNode, err) - } - defer file.Close() - - // Wrap the file in a compressor - cFile, err := st.config.Compression.Reader(file) - if err != nil { - return nil, wrap(errCorruptNode, err) - } - defer cFile.Close() - - // Read the entire file - return io.ReadAll(cFile) -} - -// WriteBytes implements Storage.WriteBytes() -func (st *BlockStorage) WriteBytes(key string, value []byte) error { - return st.WriteStream(key, bytes.NewReader(value)) -} - -// WriteStream implements Storage.WriteStream() -func (st *BlockStorage) WriteStream(key string, r io.Reader) error { - // Get node file path for key - npath, err := st.nodePathForKey(key) - if err != nil { - return err - } - - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Check if this exists - ok, err := stat(key) - if err != nil { - return err - } - - // Check if we allow overwrites - if ok && !st.config.Overwrite { - return ErrAlreadyExists - } - - // Ensure nodes dir (and any leading up to) exists - err = os.MkdirAll(st.nodePath, defaultDirPerms) - if err != nil { - return err - } - - // Ensure blocks dir (and any leading up to) exists - err = os.MkdirAll(st.blockPath, defaultDirPerms) - if err != nil { - return err - } - - // Alloc new node - node := node{} - - // Acquire HashEncoder - hc := st.hashPool.Get().(*hashEncoder) - defer st.hashPool.Put(hc) - - // Create new waitgroup and OnceError for - // goroutine error tracking and propagating - wg := sync.WaitGroup{} - onceErr := errors.OnceError{} - -loop: - for !onceErr.IsSet() { - // Fetch new buffer for this loop - buf := st.bufpool.Get() - buf.Grow(st.config.BlockSize) - - // Read next chunk - n, err := io.ReadFull(r, buf.B) - switch err { - case nil, io.ErrUnexpectedEOF: - // do nothing - case io.EOF: - st.bufpool.Put(buf) - break loop - default: - st.bufpool.Put(buf) - return err - } - - // Hash the encoded data - sum := hc.EncodeSum(buf.B) - - // Append to the node's hashes - node.hashes = append(node.hashes, sum) - - // If already on disk, skip - has, err := st.statBlock(sum) - if err != nil { - st.bufpool.Put(buf) - return err - } else if has { - st.bufpool.Put(buf) - continue loop - } - - // Check if reached EOF - atEOF := (n < buf.Len()) - - wg.Add(1) - go func() { - // Perform writes in goroutine - - defer func() { - // Defer release + - // signal we're done - st.bufpool.Put(buf) - wg.Done() - }() - - // Write block to store at hash - err = st.writeBlock(sum, buf.B[:n]) - if err != nil { - onceErr.Store(err) - return - } - }() - - // Break at end - if atEOF { - break loop - } - } - - // Wait, check errors - wg.Wait() - if onceErr.IsSet() { - return onceErr.Load() - } - - // If no hashes created, return - if len(node.hashes) < 1 { - return errNoHashesWritten - } - - // Prepare to swap error if need-be - errSwap := errSwapNoop - - // Build file RW flags - // NOTE: we performed an initial check for - // this before writing blocks, but if - // the utilizer of this storage didn't - // correctly mutex protect this key then - // someone may have beaten us to the - // punch at writing the node file. - flags := defaultFileRWFlags - if !st.config.Overwrite { - flags |= syscall.O_EXCL - - // Catch + replace err exist - errSwap = errSwapExist - } - - // Attempt to open RW file - file, err := open(npath, flags) - if err != nil { - return errSwap(err) - } - defer file.Close() - - // Acquire write buffer - buf := st.bufpool.Get() - defer st.bufpool.Put(buf) - buf.Grow(st.config.WriteBufSize) - - // Finally, write data to file - _, err = io.CopyBuffer(file, &nodeReader{node: &node}, nil) - return err -} - -// writeBlock writes the block with hash and supplied value to the filesystem -func (st *BlockStorage) writeBlock(hash string, value []byte) error { - // Get block file path for key - bpath := st.blockPathForKey(hash) - - // Attempt to open RW file - file, err := open(bpath, defaultFileRWFlags) - if err != nil { - if err == syscall.EEXIST { - err = nil /* race issue describe in struct NOTE */ - } - return err - } - defer file.Close() - - // Wrap the file in a compressor - cFile, err := st.config.Compression.Writer(file) - if err != nil { - return err - } - defer cFile.Close() - - // Write value to file - _, err = cFile.Write(value) - return err -} - -// statBlock checks for existence of supplied block hash -func (st *BlockStorage) statBlock(hash string) (bool, error) { - return stat(st.blockPathForKey(hash)) -} - -// Stat implements Storage.Stat() -func (st *BlockStorage) Stat(key string) (bool, error) { - // Get node file path for key - kpath, err := st.nodePathForKey(key) - if err != nil { - return false, err - } - - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return false, ErrClosed - } - - // Check for file on disk - return stat(kpath) -} - -// Remove implements Storage.Remove() -func (st *BlockStorage) Remove(key string) error { - // Get node file path for key - kpath, err := st.nodePathForKey(key) - if err != nil { - return err - } - - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Remove at path (we know this is file) - if err := unlink(kpath); err != nil { - return errSwapNotFound(err) - } - - return nil -} - -// Close implements Storage.Close() -func (st *BlockStorage) Close() error { - return st.lock.Close() -} - -// WalkKeys implements Storage.WalkKeys() -func (st *BlockStorage) WalkKeys(opts WalkKeysOptions) error { - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - // Walk dir for entries - return util.WalkDir(pb, st.nodePath, func(npath string, fsentry fs.DirEntry) { - // Only deal with regular files - if fsentry.Type().IsRegular() { - opts.WalkFn(entry(fsentry.Name())) - } - }) -} - -// nodePathForKey calculates the node file path for supplied key -func (st *BlockStorage) nodePathForKey(key string) (string, error) { - // Path separators are illegal, as directory paths - if strings.Contains(key, "/") || key == "." || key == ".." { - return "", ErrInvalidKey - } - - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - // Append the nodepath to key - pb.AppendString(st.nodePath) - pb.AppendString(key) - - // Return joined + cleaned node-path - return pb.Join(st.nodePath, key), nil -} - -// blockPathForKey calculates the block file path for supplied hash -func (st *BlockStorage) blockPathForKey(hash string) string { - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - return pb.Join(st.blockPath, hash) -} - -// hashSeparator is the separating byte between block hashes -const hashSeparator = byte('\n') - -// node represents the contents of a node file in storage -type node struct { - hashes []string -} - -// removeHash attempts to remove supplied block hash from the node's hash array -func (n *node) removeHash(hash string) bool { - haveDropped := false - for i := 0; i < len(n.hashes); { - if n.hashes[i] == hash { - // Drop this hash from slice - n.hashes = append(n.hashes[:i], n.hashes[i+1:]...) - haveDropped = true - } else { - // Continue iter - i++ - } - } - return haveDropped -} - -// nodeReader is an io.Reader implementation for the node file representation, -// which is useful when calculated node file is being written to the store -type nodeReader struct { - node *node - idx int - last int -} - -func (r *nodeReader) Read(b []byte) (int, error) { - n := 0 - - // '-1' means we missed writing - // hash separator on last iteration - if r.last == -1 { - b[n] = hashSeparator - n++ - r.last = 0 - } - - for r.idx < len(r.node.hashes) { - hash := r.node.hashes[r.idx] - - // Copy into buffer + update read count - m := copy(b[n:], hash[r.last:]) - n += m - - // If incomplete copy, return here - if m < len(hash)-r.last { - r.last = m - return n, nil - } - - // Check we can write last separator - if n == len(b) { - r.last = -1 - return n, nil - } - - // Write separator, iter, reset - b[n] = hashSeparator - n++ - r.idx++ - r.last = 0 - } - - // We reached end of hashes - return n, io.EOF -} - -// nodeWriter is an io.Writer implementation for the node file representation, -// which is useful when calculated node file is being read from the store -type nodeWriter struct { - node *node - buf *byteutil.Buffer -} - -func (w *nodeWriter) Write(b []byte) (int, error) { - n := 0 - - for { - // Find next hash separator position - idx := bytes.IndexByte(b[n:], hashSeparator) - if idx == -1 { - // Check we shouldn't be expecting it - if w.buf.Len() > encodedHashLen { - return n, errInvalidNode - } - - // Write all contents to buffer - w.buf.Write(b[n:]) - return len(b), nil - } - - // Found hash separator, write - // current buf contents to Node hashes - w.buf.Write(b[n : n+idx]) - n += idx + 1 - if w.buf.Len() != encodedHashLen { - return n, errInvalidNode - } - - // Append to hashes & reset - w.node.hashes = append(w.node.hashes, w.buf.String()) - w.buf.Reset() - } -} - -// blockReader is an io.Reader implementation for the combined, linked block -// data contained with a node file. Basically, this allows reading value data -// from the store for a given node file -type blockReader struct { - storage *BlockStorage - node *node - buf []byte - prev int -} - -func (r *blockReader) Read(b []byte) (int, error) { - n := 0 - - // Data left in buf, copy as much as we - // can into supplied read buffer - if r.prev < len(r.buf)-1 { - n += copy(b, r.buf[r.prev:]) - r.prev += n - if n >= len(b) { - return n, nil - } - } - - for { - // Check we have any hashes left - if len(r.node.hashes) < 1 { - return n, io.EOF - } - - // Get next key from slice - key := r.node.hashes[0] - r.node.hashes = r.node.hashes[1:] - - // Attempt to fetch next batch of data - var err error - r.buf, err = r.storage.readBlock(key) - if err != nil { - return n, err - } - r.prev = 0 - - // Copy as much as can from new buffer - m := copy(b[n:], r.buf) - r.prev += m - n += m - - // If we hit end of supplied buf, return - if n >= len(b) { - return n, nil - } - } -} - -var ( - // base64Encoding is our base64 encoding object. - base64Encoding = hashenc.Base64() - - // encodedHashLen is the once-calculated encoded hash-sum length - encodedHashLen = base64Encoding.EncodedLen( - sha256.New().Size(), - ) -) - -// hashEncoder is a HashEncoder with built-in encode buffer -type hashEncoder struct { - henc hashenc.HashEncoder - ebuf []byte -} - -// newHashEncoder returns a new hashEncoder instance -func newHashEncoder() *hashEncoder { - return &hashEncoder{ - henc: hashenc.New(sha256.New(), base64Encoding), - ebuf: make([]byte, encodedHashLen), - } -} - -// EncodeSum encodes the src data and returns resulting bytes, only valid until next call to EncodeSum() -func (henc *hashEncoder) EncodeSum(src []byte) string { - henc.henc.EncodeSum(henc.ebuf, src) - return string(henc.ebuf) -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/compressor.go b/vendor/codeberg.org/gruf/go-store/storage/compressor.go deleted file mode 100644 index d6b975db0..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/compressor.go +++ /dev/null @@ -1,104 +0,0 @@ -package storage - -import ( - "compress/gzip" - "compress/zlib" - "io" - - "codeberg.org/gruf/go-store/util" - "github.com/golang/snappy" -) - -// Compressor defines a means of compressing/decompressing values going into a key-value store -type Compressor interface { - // Reader returns a new decompressing io.ReadCloser based on supplied (compressed) io.Reader - Reader(io.Reader) (io.ReadCloser, error) - - // Writer returns a new compressing io.WriteCloser based on supplied (uncompressed) io.Writer - Writer(io.Writer) (io.WriteCloser, error) -} - -type gzipCompressor struct { - level int -} - -// GZipCompressor returns a new Compressor that implements GZip at default compression level -func GZipCompressor() Compressor { - return GZipCompressorLevel(gzip.DefaultCompression) -} - -// GZipCompressorLevel returns a new Compressor that implements GZip at supplied compression level -func GZipCompressorLevel(level int) Compressor { - return &gzipCompressor{ - level: level, - } -} - -func (c *gzipCompressor) Reader(r io.Reader) (io.ReadCloser, error) { - return gzip.NewReader(r) -} - -func (c *gzipCompressor) Writer(w io.Writer) (io.WriteCloser, error) { - return gzip.NewWriterLevel(w, c.level) -} - -type zlibCompressor struct { - level int - dict []byte -} - -// ZLibCompressor returns a new Compressor that implements ZLib at default compression level -func ZLibCompressor() Compressor { - return ZLibCompressorLevelDict(zlib.DefaultCompression, nil) -} - -// ZLibCompressorLevel returns a new Compressor that implements ZLib at supplied compression level -func ZLibCompressorLevel(level int) Compressor { - return ZLibCompressorLevelDict(level, nil) -} - -// ZLibCompressorLevelDict returns a new Compressor that implements ZLib at supplied compression level with supplied dict -func ZLibCompressorLevelDict(level int, dict []byte) Compressor { - return &zlibCompressor{ - level: level, - dict: dict, - } -} - -func (c *zlibCompressor) Reader(r io.Reader) (io.ReadCloser, error) { - return zlib.NewReaderDict(r, c.dict) -} - -func (c *zlibCompressor) Writer(w io.Writer) (io.WriteCloser, error) { - return zlib.NewWriterLevelDict(w, c.level, c.dict) -} - -type snappyCompressor struct{} - -// SnappyCompressor returns a new Compressor that implements Snappy -func SnappyCompressor() Compressor { - return &snappyCompressor{} -} - -func (c *snappyCompressor) Reader(r io.Reader) (io.ReadCloser, error) { - return util.NopReadCloser(snappy.NewReader(r)), nil -} - -func (c *snappyCompressor) Writer(w io.Writer) (io.WriteCloser, error) { - return snappy.NewBufferedWriter(w), nil -} - -type nopCompressor struct{} - -// NoCompression is a Compressor that simply does nothing -func NoCompression() Compressor { - return &nopCompressor{} -} - -func (c *nopCompressor) Reader(r io.Reader) (io.ReadCloser, error) { - return util.NopReadCloser(r), nil -} - -func (c *nopCompressor) Writer(w io.Writer) (io.WriteCloser, error) { - return util.NopWriteCloser(w), nil -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/disk.go b/vendor/codeberg.org/gruf/go-store/storage/disk.go deleted file mode 100644 index 457cc6364..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/disk.go +++ /dev/null @@ -1,400 +0,0 @@ -package storage - -import ( - "io" - "io/fs" - "os" - "path" - _path "path" - "strings" - "syscall" - - "codeberg.org/gruf/go-bytes" - "codeberg.org/gruf/go-fastcopy" - "codeberg.org/gruf/go-store/util" -) - -// DefaultDiskConfig is the default DiskStorage configuration -var DefaultDiskConfig = &DiskConfig{ - Overwrite: true, - WriteBufSize: 4096, - Transform: NopTransform(), - Compression: NoCompression(), -} - -// DiskConfig defines options to be used when opening a DiskStorage -type DiskConfig struct { - // Transform is the supplied key<-->path KeyTransform - Transform KeyTransform - - // WriteBufSize is the buffer size to use when writing file streams (PutStream) - WriteBufSize int - - // Overwrite allows overwriting values of stored keys in the storage - Overwrite bool - - // LockFile allows specifying the filesystem path to use for the lockfile, - // providing only a filename it will store the lockfile within provided store - // path and nest the store under `path/store` to prevent access to lockfile - LockFile string - - // Compression is the Compressor to use when reading / writing files, default is no compression - Compression Compressor -} - -// getDiskConfig returns a valid DiskConfig for supplied ptr -func getDiskConfig(cfg *DiskConfig) DiskConfig { - // If nil, use default - if cfg == nil { - cfg = DefaultDiskConfig - } - - // Assume nil transform == none - if cfg.Transform == nil { - cfg.Transform = NopTransform() - } - - // Assume nil compress == none - if cfg.Compression == nil { - cfg.Compression = NoCompression() - } - - // Assume 0 buf size == use default - if cfg.WriteBufSize < 1 { - cfg.WriteBufSize = DefaultDiskConfig.WriteBufSize - } - - // Assume empty lockfile path == use default - if len(cfg.LockFile) < 1 { - cfg.LockFile = LockFile - } - - // Return owned config copy - return DiskConfig{ - Transform: cfg.Transform, - WriteBufSize: cfg.WriteBufSize, - Overwrite: cfg.Overwrite, - LockFile: cfg.LockFile, - Compression: cfg.Compression, - } -} - -// DiskStorage is a Storage implementation that stores directly to a filesystem -type DiskStorage struct { - path string // path is the root path of this store - cppool fastcopy.CopyPool // cppool is the prepared io copier with buffer pool - config DiskConfig // cfg is the supplied configuration for this store - lock *Lock // lock is the opened lockfile for this storage instance -} - -// OpenFile opens a DiskStorage instance for given folder path and configuration -func OpenFile(path string, cfg *DiskConfig) (*DiskStorage, error) { - // Get checked config - config := getDiskConfig(cfg) - - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - // Clean provided store path, ensure - // ends in '/' to help later path trimming - storePath := pb.Clean(path) + "/" - - // Clean provided lockfile path - lockfile := pb.Clean(config.LockFile) - - // Check if lockfile is an *actual* path or just filename - if lockDir, _ := _path.Split(lockfile); len(lockDir) < 1 { - // Lockfile is a filename, store must be nested under - // $storePath/store to prevent access to the lockfile - storePath += "store/" - lockfile = pb.Join(path, lockfile) - } - - // Attempt to open dir path - file, err := os.OpenFile(storePath, defaultFileROFlags, defaultDirPerms) - if err != nil { - // If not a not-exist error, return - if !os.IsNotExist(err) { - return nil, err - } - - // Attempt to make store path dirs - err = os.MkdirAll(storePath, defaultDirPerms) - if err != nil { - return nil, err - } - - // Reopen dir now it's been created - file, err = os.OpenFile(storePath, defaultFileROFlags, defaultDirPerms) - if err != nil { - return nil, err - } - } - defer file.Close() - - // Double check this is a dir (NOT a file!) - stat, err := file.Stat() - if err != nil { - return nil, err - } else if !stat.IsDir() { - return nil, errPathIsFile - } - - // Open and acquire storage lock for path - lock, err := OpenLock(lockfile) - if err != nil { - return nil, err - } - - // Prepare DiskStorage - st := &DiskStorage{ - path: storePath, - config: config, - lock: lock, - } - - // Set copypool buffer size - st.cppool.Buffer(config.WriteBufSize) - - return st, nil -} - -// Clean implements Storage.Clean() -func (st *DiskStorage) Clean() error { - st.lock.Add() - defer st.lock.Done() - if st.lock.Closed() { - return ErrClosed - } - return util.CleanDirs(st.path) -} - -// ReadBytes implements Storage.ReadBytes() -func (st *DiskStorage) ReadBytes(key string) ([]byte, error) { - // Get stream reader for key - rc, err := st.ReadStream(key) - if err != nil { - return nil, err - } - defer rc.Close() - - // Read all bytes and return - return io.ReadAll(rc) -} - -// ReadStream implements Storage.ReadStream() -func (st *DiskStorage) ReadStream(key string) (io.ReadCloser, error) { - // Get file path for key - kpath, err := st.filepath(key) - if err != nil { - return nil, err - } - - // Track open - st.lock.Add() - - // Check if open - if st.lock.Closed() { - return nil, ErrClosed - } - - // Attempt to open file (replace ENOENT with our own) - file, err := open(kpath, defaultFileROFlags) - if err != nil { - st.lock.Done() - return nil, errSwapNotFound(err) - } - - // Wrap the file in a compressor - cFile, err := st.config.Compression.Reader(file) - if err != nil { - file.Close() // close this here, ignore error - st.lock.Done() - return nil, err - } - - // Wrap compressor to ensure file close - return util.ReadCloserWithCallback(cFile, func() { - file.Close() - st.lock.Done() - }), nil -} - -// WriteBytes implements Storage.WriteBytes() -func (st *DiskStorage) WriteBytes(key string, value []byte) error { - return st.WriteStream(key, bytes.NewReader(value)) -} - -// WriteStream implements Storage.WriteStream() -func (st *DiskStorage) WriteStream(key string, r io.Reader) error { - // Get file path for key - kpath, err := st.filepath(key) - if err != nil { - return err - } - - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Ensure dirs leading up to file exist - err = os.MkdirAll(path.Dir(kpath), defaultDirPerms) - if err != nil { - return err - } - - // Prepare to swap error if need-be - errSwap := errSwapNoop - - // Build file RW flags - flags := defaultFileRWFlags - if !st.config.Overwrite { - flags |= syscall.O_EXCL - - // Catch + replace err exist - errSwap = errSwapExist - } - - // Attempt to open file - file, err := open(kpath, flags) - if err != nil { - return errSwap(err) - } - defer file.Close() - - // Wrap the file in a compressor - cFile, err := st.config.Compression.Writer(file) - if err != nil { - return err - } - defer cFile.Close() - - // Copy provided reader to file - _, err = st.cppool.Copy(cFile, r) - return err -} - -// Stat implements Storage.Stat() -func (st *DiskStorage) Stat(key string) (bool, error) { - // Get file path for key - kpath, err := st.filepath(key) - if err != nil { - return false, err - } - - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return false, ErrClosed - } - - // Check for file on disk - return stat(kpath) -} - -// Remove implements Storage.Remove() -func (st *DiskStorage) Remove(key string) error { - // Get file path for key - kpath, err := st.filepath(key) - if err != nil { - return err - } - - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Remove at path (we know this is file) - if err := unlink(kpath); err != nil { - return errSwapNotFound(err) - } - - return nil -} - -// Close implements Storage.Close() -func (st *DiskStorage) Close() error { - return st.lock.Close() -} - -// WalkKeys implements Storage.WalkKeys() -func (st *DiskStorage) WalkKeys(opts WalkKeysOptions) error { - // Track open - st.lock.Add() - defer st.lock.Done() - - // Check if open - if st.lock.Closed() { - return ErrClosed - } - - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - // Walk dir for entries - return util.WalkDir(pb, st.path, func(kpath string, fsentry fs.DirEntry) { - if fsentry.Type().IsRegular() { - // Only deal with regular files - - // Get full item path (without root) - kpath = pb.Join(kpath, fsentry.Name())[len(st.path):] - - // Perform provided walk function - opts.WalkFn(entry(st.config.Transform.PathToKey(kpath))) - } - }) -} - -// filepath checks and returns a formatted filepath for given key -func (st *DiskStorage) filepath(key string) (string, error) { - // Calculate transformed key path - key = st.config.Transform.KeyToPath(key) - - // Acquire path builder - pb := util.GetPathBuilder() - defer util.PutPathBuilder(pb) - - // Generated joined root path - pb.AppendString(st.path) - pb.AppendString(key) - - // Check for dir traversal outside of root - if isDirTraversal(st.path, pb.StringPtr()) { - return "", ErrInvalidKey - } - - return pb.String(), nil -} - -// isDirTraversal will check if rootPlusPath is a dir traversal outside of root, -// assuming that both are cleaned and that rootPlusPath is path.Join(root, somePath) -func isDirTraversal(root, rootPlusPath string) bool { - switch { - // Root is $PWD, check for traversal out of - case root == ".": - return strings.HasPrefix(rootPlusPath, "../") - - // The path MUST be prefixed by root - case !strings.HasPrefix(rootPlusPath, root): - return true - - // In all other cases, check not equal - default: - return len(root) == len(rootPlusPath) - } -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/errors.go b/vendor/codeberg.org/gruf/go-store/storage/errors.go deleted file mode 100644 index 6953e11fe..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/errors.go +++ /dev/null @@ -1,90 +0,0 @@ -package storage - -import ( - "errors" - "syscall" -) - -var ( - // ErrClosed is returned on operations on a closed storage - ErrClosed = errors.New("store/storage: closed") - - // ErrNotFound is the error returned when a key cannot be found in storage - ErrNotFound = errors.New("store/storage: key not found") - - // ErrAlreadyExist is the error returned when a key already exists in storage - ErrAlreadyExists = errors.New("store/storage: key already exists") - - // ErrInvalidkey is the error returned when an invalid key is passed to storage - ErrInvalidKey = errors.New("store/storage: invalid key") - - // ErrAlreadyLocked is returned on fail opening a storage lockfile - ErrAlreadyLocked = errors.New("store/storage: storage lock already open") - - // errPathIsFile is returned when a path for a disk config is actually a file - errPathIsFile = errors.New("store/storage: path is file") - - // errNoHashesWritten is returned when no blocks are written for given input value - errNoHashesWritten = errors.New("storage/storage: no hashes written") - - // errInvalidNode is returned when read on an invalid node in the store is attempted - errInvalidNode = errors.New("store/storage: invalid node") - - // errCorruptNode is returned when a block fails to be opened / read during read of a node. - errCorruptNode = errors.New("store/storage: corrupted node") -) - -// wrappedError allows wrapping together an inner with outer error. -type wrappedError struct { - inner error - outer error -} - -// wrap will return a new wrapped error from given inner and outer errors. -func wrap(outer, inner error) *wrappedError { - return &wrappedError{ - inner: inner, - outer: outer, - } -} - -func (e *wrappedError) Is(target error) bool { - return e.outer == target || e.inner == target -} - -func (e *wrappedError) Error() string { - return e.outer.Error() + ": " + e.inner.Error() -} - -func (e *wrappedError) Unwrap() error { - return e.inner -} - -// errSwapNoop performs no error swaps -func errSwapNoop(err error) error { - return err -} - -// ErrSwapNotFound swaps syscall.ENOENT for ErrNotFound -func errSwapNotFound(err error) error { - if err == syscall.ENOENT { - return ErrNotFound - } - return err -} - -// errSwapExist swaps syscall.EEXIST for ErrAlreadyExists -func errSwapExist(err error) error { - if err == syscall.EEXIST { - return ErrAlreadyExists - } - return err -} - -// errSwapUnavailable swaps syscall.EAGAIN for ErrAlreadyLocked -func errSwapUnavailable(err error) error { - if err == syscall.EAGAIN { - return ErrAlreadyLocked - } - return err -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/fs.go b/vendor/codeberg.org/gruf/go-store/storage/fs.go deleted file mode 100644 index b4729b041..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/fs.go +++ /dev/null @@ -1,65 +0,0 @@ -package storage - -import ( - "os" - "syscall" - - "codeberg.org/gruf/go-store/util" -) - -const ( - // default file permission bits - defaultDirPerms = 0o755 - defaultFilePerms = 0o644 - - // default file open flags - defaultFileROFlags = syscall.O_RDONLY - defaultFileRWFlags = syscall.O_CREAT | syscall.O_RDWR - defaultFileLockFlags = syscall.O_RDONLY | syscall.O_CREAT -) - -// NOTE: -// These functions are for opening storage files, -// not necessarily for e.g. initial setup (OpenFile) - -// open should not be called directly. -func open(path string, flags int) (*os.File, error) { - var fd int - err := util.RetryOnEINTR(func() (err error) { - fd, err = syscall.Open(path, flags, defaultFilePerms) - return - }) - if err != nil { - return nil, err - } - return os.NewFile(uintptr(fd), path), nil -} - -// stat checks for a file on disk. -func stat(path string) (bool, error) { - var stat syscall.Stat_t - err := util.RetryOnEINTR(func() error { - return syscall.Stat(path, &stat) - }) - if err != nil { - if err == syscall.ENOENT { //nolint - err = nil - } - return false, err - } - return true, nil -} - -// unlink removes a file (not dir!) on disk. -func unlink(path string) error { - return util.RetryOnEINTR(func() error { - return syscall.Unlink(path) - }) -} - -// rmdir removes a dir (not file!) on disk. -func rmdir(path string) error { - return util.RetryOnEINTR(func() error { - return syscall.Rmdir(path) - }) -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/lock.go b/vendor/codeberg.org/gruf/go-store/storage/lock.go deleted file mode 100644 index 8a6c4c5e8..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/lock.go +++ /dev/null @@ -1,76 +0,0 @@ -package storage - -import ( - "sync" - "sync/atomic" - "syscall" - - "codeberg.org/gruf/go-store/util" -) - -// LockFile is our standard lockfile name. -const LockFile = "store.lock" - -// Lock represents a filesystem lock to ensure only one storage instance open per path. -type Lock struct { - fd int - wg sync.WaitGroup - st uint32 -} - -// OpenLock opens a lockfile at path. -func OpenLock(path string) (*Lock, error) { - var fd int - - // Open the file descriptor at path - err := util.RetryOnEINTR(func() (err error) { - fd, err = syscall.Open(path, defaultFileLockFlags, defaultFilePerms) - return - }) - if err != nil { - return nil, err - } - - // Get a flock on the file descriptor - err = util.RetryOnEINTR(func() error { - return syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB) - }) - if err != nil { - return nil, errSwapUnavailable(err) - } - - return &Lock{fd: fd}, nil -} - -// Add will add '1' to the underlying sync.WaitGroup. -func (f *Lock) Add() { - f.wg.Add(1) -} - -// Done will decrememnt '1' from the underlying sync.WaitGroup. -func (f *Lock) Done() { - f.wg.Done() -} - -// Close will attempt to close the lockfile and file descriptor. -func (f *Lock) Close() error { - var err error - if atomic.CompareAndSwapUint32(&f.st, 0, 1) { - // Wait until done - f.wg.Wait() - - // Ensure gets closed - defer syscall.Close(f.fd) - - // Call funlock on the file descriptor - err = util.RetryOnEINTR(func() error { - return syscall.Flock(f.fd, syscall.LOCK_UN|syscall.LOCK_NB) - }) - } - return err -} - -// Closed will return whether this lockfile has been closed (and unlocked). -func (f *Lock) Closed() bool { - return (atomic.LoadUint32(&f.st) == 1) -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/memory.go b/vendor/codeberg.org/gruf/go-store/storage/memory.go deleted file mode 100644 index 2dab562d6..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/memory.go +++ /dev/null @@ -1,188 +0,0 @@ -package storage - -import ( - "io" - "sync" - - "codeberg.org/gruf/go-bytes" - "codeberg.org/gruf/go-store/util" -) - -// MemoryStorage is a storage implementation that simply stores key-value -// pairs in a Go map in-memory. The map is protected by a mutex. -type MemoryStorage struct { - ow bool // overwrites - fs map[string][]byte - mu sync.Mutex - st uint32 -} - -// OpenMemory opens a new MemoryStorage instance with internal map of 'size'. -func OpenMemory(size int, overwrites bool) *MemoryStorage { - return &MemoryStorage{ - fs: make(map[string][]byte, size), - mu: sync.Mutex{}, - ow: overwrites, - } -} - -// Clean implements Storage.Clean(). -func (st *MemoryStorage) Clean() error { - st.mu.Lock() - defer st.mu.Unlock() - if st.st == 1 { - return ErrClosed - } - return nil -} - -// ReadBytes implements Storage.ReadBytes(). -func (st *MemoryStorage) ReadBytes(key string) ([]byte, error) { - // Lock storage - st.mu.Lock() - - // Check store open - if st.st == 1 { - st.mu.Unlock() - return nil, ErrClosed - } - - // Check for key - b, ok := st.fs[key] - st.mu.Unlock() - - // Return early if not exist - if !ok { - return nil, ErrNotFound - } - - // Create return copy - return bytes.Copy(b), nil -} - -// ReadStream implements Storage.ReadStream(). -func (st *MemoryStorage) ReadStream(key string) (io.ReadCloser, error) { - // Lock storage - st.mu.Lock() - - // Check store open - if st.st == 1 { - st.mu.Unlock() - return nil, ErrClosed - } - - // Check for key - b, ok := st.fs[key] - st.mu.Unlock() - - // Return early if not exist - if !ok { - return nil, ErrNotFound - } - - // Create io.ReadCloser from 'b' copy - b = bytes.Copy(b) - r := bytes.NewReader(b) - return util.NopReadCloser(r), nil -} - -// WriteBytes implements Storage.WriteBytes(). -func (st *MemoryStorage) WriteBytes(key string, b []byte) error { - // Lock storage - st.mu.Lock() - defer st.mu.Unlock() - - // Check store open - if st.st == 1 { - return ErrClosed - } - - _, ok := st.fs[key] - - // Check for already exist - if ok && !st.ow { - return ErrAlreadyExists - } - - // Write + unlock - st.fs[key] = bytes.Copy(b) - return nil -} - -// WriteStream implements Storage.WriteStream(). -func (st *MemoryStorage) WriteStream(key string, r io.Reader) error { - // Read all from reader - b, err := io.ReadAll(r) - if err != nil { - return err - } - - // Write to storage - return st.WriteBytes(key, b) -} - -// Stat implements Storage.Stat(). -func (st *MemoryStorage) Stat(key string) (bool, error) { - // Lock storage - st.mu.Lock() - defer st.mu.Unlock() - - // Check store open - if st.st == 1 { - return false, ErrClosed - } - - // Check for key - _, ok := st.fs[key] - return ok, nil -} - -// Remove implements Storage.Remove(). -func (st *MemoryStorage) Remove(key string) error { - // Lock storage - st.mu.Lock() - defer st.mu.Unlock() - - // Check store open - if st.st == 1 { - return ErrClosed - } - - // Check for key - _, ok := st.fs[key] - if !ok { - return ErrNotFound - } - - // Remove from store - delete(st.fs, key) - - return nil -} - -// Close implements Storage.Close(). -func (st *MemoryStorage) Close() error { - st.mu.Lock() - st.st = 1 - st.mu.Unlock() - return nil -} - -// WalkKeys implements Storage.WalkKeys(). -func (st *MemoryStorage) WalkKeys(opts WalkKeysOptions) error { - // Lock storage - st.mu.Lock() - defer st.mu.Unlock() - - // Check store open - if st.st == 1 { - return ErrClosed - } - - // Walk store keys - for key := range st.fs { - opts.WalkFn(entry(key)) - } - - return nil -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/storage.go b/vendor/codeberg.org/gruf/go-store/storage/storage.go deleted file mode 100644 index 346aff097..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/storage.go +++ /dev/null @@ -1,54 +0,0 @@ -package storage - -import ( - "io" -) - -// StorageEntry defines a key in Storage -type StorageEntry interface { - // Key returns the storage entry's key - Key() string -} - -// entry is the simplest possible StorageEntry -type entry string - -func (e entry) Key() string { - return string(e) -} - -// Storage defines a means of storing and accessing key value pairs -type Storage interface { - // ReadBytes returns the byte value for key in storage - ReadBytes(key string) ([]byte, error) - - // ReadStream returns an io.ReadCloser for the value bytes at key in the storage - ReadStream(key string) (io.ReadCloser, error) - - // WriteBytes writes the supplied value bytes at key in the storage - WriteBytes(key string, value []byte) error - - // WriteStream writes the bytes from supplied reader at key in the storage - WriteStream(key string, r io.Reader) error - - // Stat checks if the supplied key is in the storage - Stat(key string) (bool, error) - - // Remove attempts to remove the supplied key-value pair from storage - Remove(key string) error - - // Close will close the storage, releasing any file locks - Close() error - - // Clean removes unused values and unclutters the storage (e.g. removing empty folders) - Clean() error - - // WalkKeys walks the keys in the storage - WalkKeys(opts WalkKeysOptions) error -} - -// WalkKeysOptions defines how to walk the keys in a storage implementation -type WalkKeysOptions struct { - // WalkFn is the function to apply on each StorageEntry - WalkFn func(StorageEntry) -} diff --git a/vendor/codeberg.org/gruf/go-store/storage/transform.go b/vendor/codeberg.org/gruf/go-store/storage/transform.go deleted file mode 100644 index 3863dd774..000000000 --- a/vendor/codeberg.org/gruf/go-store/storage/transform.go +++ /dev/null @@ -1,25 +0,0 @@ -package storage - -// KeyTransform defines a method of converting store keys to storage paths (and vice-versa) -type KeyTransform interface { - // KeyToPath converts a supplied key to storage path - KeyToPath(string) string - - // PathToKey converts a supplied storage path to key - PathToKey(string) string -} - -type nopKeyTransform struct{} - -// NopTransform returns a nop key transform (i.e. key = path) -func NopTransform() KeyTransform { - return &nopKeyTransform{} -} - -func (t *nopKeyTransform) KeyToPath(key string) string { - return key -} - -func (t *nopKeyTransform) PathToKey(path string) string { - return path -} |