github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/lib/encoder/filename/encode.go (about) 1 package filename 2 3 import ( 4 "encoding/base64" 5 "encoding/binary" 6 7 "github.com/dop251/scsu" 8 "github.com/klauspost/compress/huff0" 9 ) 10 11 // Encode will encode the string and return a base64 (url) compatible version of it. 12 // Calling Decode with the returned string should always succeed. 13 // It is not a requirement that the input string is valid utf-8. 14 func Encode(s string) string { 15 table, payload := EncodeBytes(s) 16 return string(encodeURL[table]) + base64.URLEncoding.EncodeToString(payload) 17 } 18 19 // EncodeBytes will compress the given string and return a table identifier and a payload. 20 func EncodeBytes(s string) (table byte, payload []byte) { 21 initCoders() 22 bestSize := len(s) 23 bestTable := byte(tableUncompressed) 24 org := []byte(s) 25 bestOut := []byte(s) 26 // Try all tables and choose the best 27 for i, enc := range encTables[:] { 28 org := org 29 if len(org) <= 1 || len(org) > maxLength { 30 // Use the uncompressed 31 break 32 } 33 34 if enc == nil { 35 continue 36 } 37 38 if i == tableSCSU { 39 var err error 40 olen := len(org) 41 org, err = scsu.EncodeStrict(s, make([]byte, 0, len(org))) 42 if err != nil || olen <= len(org) { 43 continue 44 } 45 if len(org) < bestSize { 46 // This is already better, store so we can use if the table cannot. 47 bestOut = bestOut[:len(org)] 48 bestTable = tableSCSUPlain 49 bestSize = len(org) 50 copy(bestOut, org) 51 } 52 } 53 54 // Try to encode using table. 55 err := func() error { 56 encTableLocks[i].Lock() 57 defer encTableLocks[i].Unlock() 58 out, _, err := huff0.Compress1X(org, enc) 59 if err != nil { 60 return err 61 } 62 if len(out) < bestSize { 63 bestOut = bestOut[:len(out)] 64 bestTable = byte(i) 65 bestSize = len(out) 66 copy(bestOut, out) 67 } 68 return nil 69 }() 70 // If input is a single byte repeated store as RLE or save uncompressed. 71 if err == huff0.ErrUseRLE && i != tableSCSU { 72 if len(org) > 2 { 73 // Encode as one byte repeated since it will be smaller than uncompressed. 74 n := binary.PutUvarint(bestOut, uint64(len(org))) 75 bestOut = bestOut[:n+1] 76 bestOut[n] = org[0] 77 bestSize = n + 1 78 bestTable = tableRLE 79 } 80 break 81 } 82 } 83 84 return bestTable, bestOut 85 }