cb7534dcdf
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
84 lines
2 KiB
Go
84 lines
2 KiB
Go
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
|
|
}
|
|
}
|