Skip to content

Commit

Permalink
Merge pull request #6 from godtoy/master
Browse files Browse the repository at this point in the history
improved performance
  • Loading branch information
lazarus authored Apr 15, 2021
2 parents 6a369d4 + 05b3afe commit 9ede4c0
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
# lz-string-go

Golang implementation of the [LZ-based compression algorithm](https://www.npmjs.com/package/lz-string).

## Improvements
- [Performance problems](https://github.com/austinh115/lz-string-go/issues/5) | [PR](https://github.com/austinh115/lz-string-go/pull/6)
- Performance optimizations have been made to this library
- Decompression support for drop characters
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module lz-string-go

go 1.16
135 changes: 95 additions & 40 deletions lz-string.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@ package LZString
import (
"errors"
"math"
"strings"
"sync"
"unicode/utf8"
)

// Compress
const _defaultKeyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

func CompressToBase64(uncompressed string) string {
func Compress(uncompressed string, keyStrBase64 string) string {
if len(uncompressed) == 0 {
return ""
}

res := Compress(uncompressed, 6, func(character int) string {
return string([]rune(keyStrBase64)[character])
if keyStrBase64 == "" {
keyStrBase64 = _defaultKeyStrBase64
}
charArr := []rune(keyStrBase64)
res := _compress(uncompressed, 6, func(character int) string {
return string(charArr[character])
})

switch len(res) % 4 {
default:
case 0:
Expand All @@ -33,7 +36,7 @@ func CompressToBase64(uncompressed string) string {
return res
}

func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(character int) string) string {
func _compress(uncompressed string, bitsPerChar int, getCharFromInt func(character int) string) string {
if len(uncompressed) == 0 {
return ""
}
Expand All @@ -46,44 +49,48 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextEnlargeIn := float64(2)
contextDictSize := int(3)
contextNumBits := int(2)
var contextDataString string
//var contextDataString string
var contextDataString = strings.Builder{}

contextDataVal := int(0)
contextDataPosition := int(0)

for ii := 0; ii < len([]rune(uncompressed)); ii++ {
contextC = string([]rune(uncompressed)[ii])
uncompressedRunes := []rune(uncompressed)
for ii := 0; ii < len(uncompressedRunes); ii++ {
contextC = string(uncompressedRunes[ii])
_, in := contextDictionary[contextC]
if !in {
contextDictionary[contextC] = contextDictSize
contextDictSize++
contextDictionaryToCreate[contextC] = true
}

contextWc = contextW + contextC

_, in = contextDictionary[contextWc]
if in {
contextW = contextWc
} else {
_, in = contextDictionaryToCreate[contextW]
if in {
if []rune(contextW)[0] < 256 {
contextWRune := []rune(contextW)
if contextWRune[0] < 256 {
for i := 0; i < contextNumBits; i++ {
contextDataVal = contextDataVal << 1
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
}
}
value = int([]rune(contextW)[0])
value = int(contextWRune[0])
for i := 0; i < 8; i++ {
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -96,19 +103,21 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | value
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
}
value = 0
}
value = int([]rune(contextW)[0])
value = int(contextWRune[0])
for i := 0; i < 16; i++ {
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -128,7 +137,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -155,7 +165,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = contextDataVal << 1
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -166,7 +177,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -179,7 +191,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | value
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -191,7 +204,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -211,7 +225,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -231,7 +246,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
contextDataVal = (contextDataVal << 1) | (value & 1)
if contextDataPosition == bitsPerChar-1 {
contextDataPosition = 0
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
contextDataVal = 0
} else {
contextDataPosition++
Expand All @@ -242,21 +258,22 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
for {
contextDataVal = contextDataVal << 1
if contextDataPosition == bitsPerChar-1 {
contextDataString += getCharFromInt(contextDataVal)
//contextDataString += getCharFromInt(contextDataVal)
contextDataString.WriteString(getCharFromInt(contextDataVal))
break
} else {
contextDataPosition++
}
}
return contextDataString
return contextDataString.String()
}

// Decompress

var keyStrBase64Map map[byte]int = map[byte]int{74: 9, 78: 13, 83: 18, 61: 64, 109: 38, 114: 43, 116: 45, 101: 30, 47: 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}
var baseReverseDic = sync.Map{}

type dataStruct struct {
input string
alphabet string
val int
position int
index int
Expand All @@ -265,8 +282,25 @@ type dataStruct struct {
numBits int
}

func getBaseValue(char byte) int {
return keyStrBase64Map[char]
func covertToBaseReverseDic(alphabet string) map[byte]int {
var val = map[byte]int{}
charArr := []rune(alphabet)
for i := 0; i < len(charArr); i++ {
val[byte(charArr[i])] = i
}
return val
}

func getBaseValue(alphabet string, char byte) int {
vv, ok := baseReverseDic.Load(alphabet)
var arr map[byte]int
if ok {
arr = vv.(map[byte]int)
} else {
arr = covertToBaseReverseDic(alphabet)
baseReverseDic.Store(alphabet, arr)
}
return arr[char]
}

// Input is composed of ASCII characters, so accessing it by array has no UTF-8 pb.
Expand All @@ -278,7 +312,7 @@ func readBits(nb int, data *dataStruct) int {
data.position = data.position / 2
if data.position == 0 {
data.position = 32
data.val = getBaseValue(data.input[data.index])
data.val = getBaseValue(data.alphabet, data.input[data.index])
data.index += 1
}
if respB > 0 {
Expand All @@ -302,11 +336,11 @@ func getString(last string, data *dataStruct) (string, bool, error) {
c := readBits(data.numBits, data)
switch c {
case 0:
str := string(readBits(8, data))
str := string(rune(readBits(8, data)))
appendValue(data, str)
return str, false, nil
case 1:
str := string(readBits(16, data))
str := string(rune(readBits(16, data)))
appendValue(data, str)
return str, false, nil
case 2:
Expand All @@ -327,9 +361,11 @@ func concatWithFirstRune(str string, getFirstRune string) string {
return str + string(r)
}

func DecompressFromBase64(input string) (string, error) {
data := dataStruct{input, getBaseValue(input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2}

func Decompress(input string, keyStrBase64 string) (string, error) {
if keyStrBase64 == "" {
keyStrBase64 = _defaultKeyStrBase64
}
data := dataStruct{input, keyStrBase64, getBaseValue(keyStrBase64, input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2}
result, isEnd, err := getString("", &data)
if err != nil || isEnd {
return result, err
Expand All @@ -341,9 +377,28 @@ func DecompressFromBase64(input string) (string, error) {
if err != nil || isEnd {
return result, err
}

result = result + str
appendValue(&data, concatWithFirstRune(last, str))
last = str
}
}

//func DecompressFromBase64New(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
// newResBuf := strings.Builder{}
// for {
// str, isEnd, err := getString(last, &data)
// if err != nil || isEnd {
// return newResBuf.String(), err
// }
// newResBuf.WriteString(str)
// appendValue(&data, concatWithFirstRune(last, str))
// last = str
// }
//}
Loading

0 comments on commit 9ede4c0

Please sign in to comment.