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  }