2020-08-13 17:14:11 +02:00
|
|
|
package filename
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/binary"
|
|
|
|
|
2021-08-20 19:05:14 +02:00
|
|
|
"github.com/dop251/scsu"
|
2020-08-13 17:14:11 +02:00
|
|
|
"github.com/klauspost/compress/huff0"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Encode will encode the string and return a base64 (url) compatible version of it.
|
|
|
|
// Calling Decode with the returned string should always succeed.
|
|
|
|
// It is not a requirement that the input string is valid utf-8.
|
|
|
|
func Encode(s string) string {
|
2021-01-04 17:09:09 +01:00
|
|
|
table, payload := EncodeBytes(s)
|
|
|
|
return string(encodeURL[table]) + base64.URLEncoding.EncodeToString(payload)
|
|
|
|
}
|
|
|
|
|
|
|
|
// EncodeBytes will compress the given string and return a table identifier and a payload.
|
|
|
|
func EncodeBytes(s string) (table byte, payload []byte) {
|
2020-08-13 17:14:11 +02:00
|
|
|
initCoders()
|
|
|
|
bestSize := len(s)
|
2021-01-04 17:09:09 +01:00
|
|
|
bestTable := byte(tableUncompressed)
|
2020-08-13 17:14:11 +02:00
|
|
|
org := []byte(s)
|
|
|
|
bestOut := []byte(s)
|
|
|
|
// Try all tables and choose the best
|
|
|
|
for i, enc := range encTables[:] {
|
2021-01-04 17:09:09 +01:00
|
|
|
org := org
|
2020-08-13 17:14:11 +02:00
|
|
|
if len(org) <= 1 || len(org) > maxLength {
|
|
|
|
// Use the uncompressed
|
|
|
|
break
|
|
|
|
}
|
2021-01-04 17:09:09 +01:00
|
|
|
|
2020-08-13 17:14:11 +02:00
|
|
|
if enc == nil {
|
|
|
|
continue
|
|
|
|
}
|
2021-01-04 17:09:09 +01:00
|
|
|
|
|
|
|
if i == tableSCSU {
|
|
|
|
var err error
|
|
|
|
olen := len(org)
|
2021-08-20 19:05:14 +02:00
|
|
|
org, err = scsu.EncodeStrict(s, make([]byte, 0, len(org)))
|
2021-01-04 17:09:09 +01:00
|
|
|
if err != nil || olen <= len(org) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(org) < bestSize {
|
|
|
|
// This is already better, store so we can use if the table cannot.
|
|
|
|
bestOut = bestOut[:len(org)]
|
|
|
|
bestTable = tableSCSUPlain
|
|
|
|
bestSize = len(org)
|
|
|
|
copy(bestOut, org)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-13 17:14:11 +02:00
|
|
|
// Try to encode using table.
|
|
|
|
err := func() error {
|
|
|
|
encTableLocks[i].Lock()
|
|
|
|
defer encTableLocks[i].Unlock()
|
|
|
|
out, _, err := huff0.Compress1X(org, enc)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(out) < bestSize {
|
|
|
|
bestOut = bestOut[:len(out)]
|
2021-01-04 17:09:09 +01:00
|
|
|
bestTable = byte(i)
|
2020-08-13 17:14:11 +02:00
|
|
|
bestSize = len(out)
|
|
|
|
copy(bestOut, out)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}()
|
|
|
|
// If input is a single byte repeated store as RLE or save uncompressed.
|
2021-01-04 17:09:09 +01:00
|
|
|
if err == huff0.ErrUseRLE && i != tableSCSU {
|
2020-08-13 17:14:11 +02:00
|
|
|
if len(org) > 2 {
|
|
|
|
// Encode as one byte repeated since it will be smaller than uncompressed.
|
|
|
|
n := binary.PutUvarint(bestOut, uint64(len(org)))
|
|
|
|
bestOut = bestOut[:n+1]
|
|
|
|
bestOut[n] = org[0]
|
|
|
|
bestSize = n + 1
|
|
|
|
bestTable = tableRLE
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-04 17:09:09 +01:00
|
|
|
return bestTable, bestOut
|
2020-08-13 17:14:11 +02:00
|
|
|
}
|