111 lines
3.0 KiB
Go
111 lines
3.0 KiB
Go
package helper
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
//
|
|
// Decompress uri encoded lz-string
|
|
// http://pieroxy.net/blog/pages/lz-string/index.html
|
|
// https://github.com/pieroxy/lz-string/
|
|
//
|
|
|
|
// map of "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"
|
|
var keyStrUriSafe map[byte]int = map[byte]int{74: 9, 78: 13, 83: 18, 36: 64, 109: 38, 114: 43, 116: 45, 101: 30, 45: 63, 73: 8, 81: 16, 113: 42, 49: 53, 50: 54, 54: 58, 76: 11, 100: 29, 107: 36, 121: 50, 77: 12, 89: 24, 105: 34, 66: 1, 69: 4, 85: 20, 48: 52, 119: 48, 117: 46, 120: 49, 52: 56, 56: 60, 110: 39, 112: 41, 70: 5, 71: 6, 79: 14, 88: 23, 97: 26, 102: 31, 103: 32, 67: 2, 118: 47, 65: 0, 68: 3, 72: 7, 108: 37, 51: 55, 57: 61, 82: 17, 90: 25, 98: 27, 115: 44, 122: 51, 53: 57, 86: 21, 106: 35, 111: 40, 55: 59, 43: 62, 75: 10, 80: 15, 84: 19, 87: 22, 99: 28, 104: 33}
|
|
|
|
type dataStruct struct {
|
|
input string
|
|
val int
|
|
position int
|
|
index int
|
|
dictionary []string
|
|
enlargeIn float64
|
|
numBits int
|
|
}
|
|
|
|
func getBaseValue(char byte) int {
|
|
return keyStrUriSafe[char]
|
|
}
|
|
|
|
// Input is composed of ASCII characters, so accessing it by array has no UTF-8 pb.
|
|
func readBits(nb int, data *dataStruct) int {
|
|
result := 0
|
|
power := 1
|
|
for i := 0; i < nb; i++ {
|
|
respB := data.val & data.position
|
|
data.position = data.position / 2
|
|
if data.position == 0 {
|
|
data.position = 32
|
|
data.val = getBaseValue(data.input[data.index])
|
|
data.index += 1
|
|
}
|
|
if respB > 0 {
|
|
result |= power
|
|
}
|
|
power *= 2
|
|
}
|
|
return result
|
|
}
|
|
|
|
func appendValue(data *dataStruct, str string) {
|
|
data.dictionary = append(data.dictionary, str)
|
|
data.enlargeIn -= 1
|
|
if data.enlargeIn == 0 {
|
|
data.enlargeIn = math.Pow(2, float64(data.numBits))
|
|
data.numBits += 1
|
|
}
|
|
}
|
|
|
|
func getString(last string, data *dataStruct) (string, bool, error) {
|
|
c := readBits(data.numBits, data)
|
|
switch c {
|
|
case 0:
|
|
str := string(readBits(8, data))
|
|
appendValue(data, str)
|
|
return str, false, nil
|
|
case 1:
|
|
str := string(readBits(16, data))
|
|
appendValue(data, str)
|
|
return str, false, nil
|
|
case 2:
|
|
return "", true, nil
|
|
}
|
|
if c < len(data.dictionary) {
|
|
return data.dictionary[c], false, nil
|
|
}
|
|
if c == len(data.dictionary) {
|
|
return concatWithFirstRune(last, last), false, nil
|
|
}
|
|
return "", false, errors.New("Bad character encoding.")
|
|
}
|
|
|
|
// Need to handle UTF-8, so we need to use rune to concatenate
|
|
func concatWithFirstRune(str string, getFirstRune string) string {
|
|
r, _ := utf8.DecodeRuneInString(getFirstRune)
|
|
return str + string(r)
|
|
}
|
|
|
|
func DecompressFromEncodedUriComponent(input string) (string, error) {
|
|
data := dataStruct{input, getBaseValue(input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2}
|
|
|
|
result, isEnd, err := getString("", &data)
|
|
if err != nil || isEnd {
|
|
return result, err
|
|
}
|
|
last := result
|
|
data.numBits += 1
|
|
for {
|
|
str, isEnd, err := getString(last, &data)
|
|
if err != nil || isEnd {
|
|
return result, err
|
|
}
|
|
|
|
result = result + str
|
|
appendValue(&data, concatWithFirstRune(last, str))
|
|
last = str
|
|
}
|
|
|
|
return "", errors.New("Unexpected end of buffer reached.")
|
|
} |