lib: Add file name compression
Allows to compress short arbitrary strings and returns a string using base64 url encoding. Generator for tables included and a few samples has been added. Add more to init.go Tested with fuzzing for crash resistance and symmetry, see fuzz.go
This commit is contained in:
parent
770a6f2cad
commit
cb7534dcdf
8 changed files with 363 additions and 4 deletions
84
lib/encoder/filename/decode.go
Normal file
84
lib/encoder/filename/decode.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package filename
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/klauspost/compress/huff0"
|
||||
)
|
||||
|
||||
// ErrCorrupted is returned if a provided encoded filename cannot be decoded.
|
||||
var ErrCorrupted = errors.New("file name corrupt")
|
||||
|
||||
// ErrUnsupported is returned if a provided encoding may come from a future version or the file name is corrupt.
|
||||
var ErrUnsupported = errors.New("file name possibly generated by future version of rclone")
|
||||
|
||||
// Custom decoder for tableCustom types. Stateful, so must have lock.
|
||||
var customDec huff0.Scratch
|
||||
var customDecMu sync.Mutex
|
||||
|
||||
// Decode an encoded string.
|
||||
func Decode(s string) (string, error) {
|
||||
if len(s) < 1 {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
table := decodeMap[s[0]]
|
||||
if table == 0 {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
table--
|
||||
s = s[1:]
|
||||
|
||||
data := make([]byte, base64.URLEncoding.DecodedLen(len(s)))
|
||||
n, err := base64.URLEncoding.Decode(data, ([]byte)(s))
|
||||
if err != nil || n < 0 {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
data = data[:n]
|
||||
|
||||
switch table {
|
||||
case tableUncompressed:
|
||||
return string(data), nil
|
||||
case tableReserved:
|
||||
return "", ErrUnsupported
|
||||
case tableRLE:
|
||||
if len(data) < 2 {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
n, used := binary.Uvarint(data[:len(data)-1])
|
||||
if used <= 0 || n > maxLength {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
return string(bytes.Repeat(data[len(data)-1:], int(n))), nil
|
||||
case tableCustom:
|
||||
customDecMu.Lock()
|
||||
defer customDecMu.Unlock()
|
||||
_, data, err := huff0.ReadTable(data, &customDec)
|
||||
if err != nil {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
customDec.MaxDecodedSize = maxLength
|
||||
decoded, err := customDec.Decompress1X(data)
|
||||
if err != nil {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
return string(decoded), nil
|
||||
default:
|
||||
if table >= byte(len(decTables)) {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
dec := decTables[table]
|
||||
if dec == nil {
|
||||
return "", ErrUnsupported
|
||||
}
|
||||
var dst [maxLength]byte
|
||||
name, err := dec.Decompress1X(dst[:0], data)
|
||||
if err != nil {
|
||||
return "", ErrCorrupted
|
||||
}
|
||||
return string(name), nil
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue