mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 01:44:41 +01:00
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
85 lines
2.0 KiB
Go
85 lines
2.0 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
|
|
}
|
|
}
|