Skip to content

Commit 9ede4c0

Browse files
authored
Merge pull request #6 from godtoy/master
improved performance
2 parents 6a369d4 + 05b3afe commit 9ede4c0

File tree

5 files changed

+215
-40
lines changed

5 files changed

+215
-40
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
11
# lz-string-go
2+
3+
Golang implementation of the [LZ-based compression algorithm](https://www.npmjs.com/package/lz-string).
4+
5+
## Improvements
6+
- [Performance problems](https://github.com/austinh115/lz-string-go/issues/5) | [PR](https://github.com/austinh115/lz-string-go/pull/6)
7+
- Performance optimizations have been made to this library
8+
- Decompression support for drop characters

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module lz-string-go
2+
3+
go 1.16

lz-string.go

Lines changed: 95 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,25 @@ package LZString
33
import (
44
"errors"
55
"math"
6+
"strings"
7+
"sync"
68
"unicode/utf8"
79
)
810

911
// Compress
12+
const _defaultKeyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
1013

11-
var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
12-
13-
func CompressToBase64(uncompressed string) string {
14+
func Compress(uncompressed string, keyStrBase64 string) string {
1415
if len(uncompressed) == 0 {
1516
return ""
1617
}
17-
18-
res := Compress(uncompressed, 6, func(character int) string {
19-
return string([]rune(keyStrBase64)[character])
18+
if keyStrBase64 == "" {
19+
keyStrBase64 = _defaultKeyStrBase64
20+
}
21+
charArr := []rune(keyStrBase64)
22+
res := _compress(uncompressed, 6, func(character int) string {
23+
return string(charArr[character])
2024
})
21-
2225
switch len(res) % 4 {
2326
default:
2427
case 0:
@@ -33,7 +36,7 @@ func CompressToBase64(uncompressed string) string {
3336
return res
3437
}
3538

36-
func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(character int) string) string {
39+
func _compress(uncompressed string, bitsPerChar int, getCharFromInt func(character int) string) string {
3740
if len(uncompressed) == 0 {
3841
return ""
3942
}
@@ -46,44 +49,48 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
4649
contextEnlargeIn := float64(2)
4750
contextDictSize := int(3)
4851
contextNumBits := int(2)
49-
var contextDataString string
52+
//var contextDataString string
53+
var contextDataString = strings.Builder{}
54+
5055
contextDataVal := int(0)
5156
contextDataPosition := int(0)
5257

53-
for ii := 0; ii < len([]rune(uncompressed)); ii++ {
54-
contextC = string([]rune(uncompressed)[ii])
58+
uncompressedRunes := []rune(uncompressed)
59+
for ii := 0; ii < len(uncompressedRunes); ii++ {
60+
contextC = string(uncompressedRunes[ii])
5561
_, in := contextDictionary[contextC]
5662
if !in {
5763
contextDictionary[contextC] = contextDictSize
5864
contextDictSize++
5965
contextDictionaryToCreate[contextC] = true
6066
}
61-
6267
contextWc = contextW + contextC
63-
6468
_, in = contextDictionary[contextWc]
6569
if in {
6670
contextW = contextWc
6771
} else {
6872
_, in = contextDictionaryToCreate[contextW]
6973
if in {
70-
if []rune(contextW)[0] < 256 {
74+
contextWRune := []rune(contextW)
75+
if contextWRune[0] < 256 {
7176
for i := 0; i < contextNumBits; i++ {
7277
contextDataVal = contextDataVal << 1
7378
if contextDataPosition == bitsPerChar-1 {
7479
contextDataPosition = 0
75-
contextDataString += getCharFromInt(contextDataVal)
80+
//contextDataString += getCharFromInt(contextDataVal)
81+
contextDataString.WriteString(getCharFromInt(contextDataVal))
7682
contextDataVal = 0
7783
} else {
7884
contextDataPosition++
7985
}
8086
}
81-
value = int([]rune(contextW)[0])
87+
value = int(contextWRune[0])
8288
for i := 0; i < 8; i++ {
8389
contextDataVal = (contextDataVal << 1) | (value & 1)
8490
if contextDataPosition == bitsPerChar-1 {
8591
contextDataPosition = 0
86-
contextDataString += getCharFromInt(contextDataVal)
92+
//contextDataString += getCharFromInt(contextDataVal)
93+
contextDataString.WriteString(getCharFromInt(contextDataVal))
8794
contextDataVal = 0
8895
} else {
8996
contextDataPosition++
@@ -96,19 +103,21 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
96103
contextDataVal = (contextDataVal << 1) | value
97104
if contextDataPosition == bitsPerChar-1 {
98105
contextDataPosition = 0
99-
contextDataString += getCharFromInt(contextDataVal)
106+
//contextDataString += getCharFromInt(contextDataVal)
107+
contextDataString.WriteString(getCharFromInt(contextDataVal))
100108
contextDataVal = 0
101109
} else {
102110
contextDataPosition++
103111
}
104112
value = 0
105113
}
106-
value = int([]rune(contextW)[0])
114+
value = int(contextWRune[0])
107115
for i := 0; i < 16; i++ {
108116
contextDataVal = (contextDataVal << 1) | (value & 1)
109117
if contextDataPosition == bitsPerChar-1 {
110118
contextDataPosition = 0
111-
contextDataString += getCharFromInt(contextDataVal)
119+
//contextDataString += getCharFromInt(contextDataVal)
120+
contextDataString.WriteString(getCharFromInt(contextDataVal))
112121
contextDataVal = 0
113122
} else {
114123
contextDataPosition++
@@ -128,7 +137,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
128137
contextDataVal = (contextDataVal << 1) | (value & 1)
129138
if contextDataPosition == bitsPerChar-1 {
130139
contextDataPosition = 0
131-
contextDataString += getCharFromInt(contextDataVal)
140+
//contextDataString += getCharFromInt(contextDataVal)
141+
contextDataString.WriteString(getCharFromInt(contextDataVal))
132142
contextDataVal = 0
133143
} else {
134144
contextDataPosition++
@@ -155,7 +165,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
155165
contextDataVal = contextDataVal << 1
156166
if contextDataPosition == bitsPerChar-1 {
157167
contextDataPosition = 0
158-
contextDataString += getCharFromInt(contextDataVal)
168+
//contextDataString += getCharFromInt(contextDataVal)
169+
contextDataString.WriteString(getCharFromInt(contextDataVal))
159170
contextDataVal = 0
160171
} else {
161172
contextDataPosition++
@@ -166,7 +177,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
166177
contextDataVal = (contextDataVal << 1) | (value & 1)
167178
if contextDataPosition == bitsPerChar-1 {
168179
contextDataPosition = 0
169-
contextDataString += getCharFromInt(contextDataVal)
180+
//contextDataString += getCharFromInt(contextDataVal)
181+
contextDataString.WriteString(getCharFromInt(contextDataVal))
170182
contextDataVal = 0
171183
} else {
172184
contextDataPosition++
@@ -179,7 +191,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
179191
contextDataVal = (contextDataVal << 1) | value
180192
if contextDataPosition == bitsPerChar-1 {
181193
contextDataPosition = 0
182-
contextDataString += getCharFromInt(contextDataVal)
194+
//contextDataString += getCharFromInt(contextDataVal)
195+
contextDataString.WriteString(getCharFromInt(contextDataVal))
183196
contextDataVal = 0
184197
} else {
185198
contextDataPosition++
@@ -191,7 +204,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
191204
contextDataVal = (contextDataVal << 1) | (value & 1)
192205
if contextDataPosition == bitsPerChar-1 {
193206
contextDataPosition = 0
194-
contextDataString += getCharFromInt(contextDataVal)
207+
//contextDataString += getCharFromInt(contextDataVal)
208+
contextDataString.WriteString(getCharFromInt(contextDataVal))
195209
contextDataVal = 0
196210
} else {
197211
contextDataPosition++
@@ -211,7 +225,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
211225
contextDataVal = (contextDataVal << 1) | (value & 1)
212226
if contextDataPosition == bitsPerChar-1 {
213227
contextDataPosition = 0
214-
contextDataString += getCharFromInt(contextDataVal)
228+
//contextDataString += getCharFromInt(contextDataVal)
229+
contextDataString.WriteString(getCharFromInt(contextDataVal))
215230
contextDataVal = 0
216231
} else {
217232
contextDataPosition++
@@ -231,7 +246,8 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
231246
contextDataVal = (contextDataVal << 1) | (value & 1)
232247
if contextDataPosition == bitsPerChar-1 {
233248
contextDataPosition = 0
234-
contextDataString += getCharFromInt(contextDataVal)
249+
//contextDataString += getCharFromInt(contextDataVal)
250+
contextDataString.WriteString(getCharFromInt(contextDataVal))
235251
contextDataVal = 0
236252
} else {
237253
contextDataPosition++
@@ -242,21 +258,22 @@ func Compress(uncompressed string, bitsPerChar int, getCharFromInt func(characte
242258
for {
243259
contextDataVal = contextDataVal << 1
244260
if contextDataPosition == bitsPerChar-1 {
245-
contextDataString += getCharFromInt(contextDataVal)
261+
//contextDataString += getCharFromInt(contextDataVal)
262+
contextDataString.WriteString(getCharFromInt(contextDataVal))
246263
break
247264
} else {
248265
contextDataPosition++
249266
}
250267
}
251-
return contextDataString
268+
return contextDataString.String()
252269
}
253270

254271
// Decompress
255-
256-
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}
272+
var baseReverseDic = sync.Map{}
257273

258274
type dataStruct struct {
259275
input string
276+
alphabet string
260277
val int
261278
position int
262279
index int
@@ -265,8 +282,25 @@ type dataStruct struct {
265282
numBits int
266283
}
267284

268-
func getBaseValue(char byte) int {
269-
return keyStrBase64Map[char]
285+
func covertToBaseReverseDic(alphabet string) map[byte]int {
286+
var val = map[byte]int{}
287+
charArr := []rune(alphabet)
288+
for i := 0; i < len(charArr); i++ {
289+
val[byte(charArr[i])] = i
290+
}
291+
return val
292+
}
293+
294+
func getBaseValue(alphabet string, char byte) int {
295+
vv, ok := baseReverseDic.Load(alphabet)
296+
var arr map[byte]int
297+
if ok {
298+
arr = vv.(map[byte]int)
299+
} else {
300+
arr = covertToBaseReverseDic(alphabet)
301+
baseReverseDic.Store(alphabet, arr)
302+
}
303+
return arr[char]
270304
}
271305

272306
// Input is composed of ASCII characters, so accessing it by array has no UTF-8 pb.
@@ -278,7 +312,7 @@ func readBits(nb int, data *dataStruct) int {
278312
data.position = data.position / 2
279313
if data.position == 0 {
280314
data.position = 32
281-
data.val = getBaseValue(data.input[data.index])
315+
data.val = getBaseValue(data.alphabet, data.input[data.index])
282316
data.index += 1
283317
}
284318
if respB > 0 {
@@ -302,11 +336,11 @@ func getString(last string, data *dataStruct) (string, bool, error) {
302336
c := readBits(data.numBits, data)
303337
switch c {
304338
case 0:
305-
str := string(readBits(8, data))
339+
str := string(rune(readBits(8, data)))
306340
appendValue(data, str)
307341
return str, false, nil
308342
case 1:
309-
str := string(readBits(16, data))
343+
str := string(rune(readBits(16, data)))
310344
appendValue(data, str)
311345
return str, false, nil
312346
case 2:
@@ -327,9 +361,11 @@ func concatWithFirstRune(str string, getFirstRune string) string {
327361
return str + string(r)
328362
}
329363

330-
func DecompressFromBase64(input string) (string, error) {
331-
data := dataStruct{input, getBaseValue(input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2}
332-
364+
func Decompress(input string, keyStrBase64 string) (string, error) {
365+
if keyStrBase64 == "" {
366+
keyStrBase64 = _defaultKeyStrBase64
367+
}
368+
data := dataStruct{input, keyStrBase64, getBaseValue(keyStrBase64, input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2}
333369
result, isEnd, err := getString("", &data)
334370
if err != nil || isEnd {
335371
return result, err
@@ -341,9 +377,28 @@ func DecompressFromBase64(input string) (string, error) {
341377
if err != nil || isEnd {
342378
return result, err
343379
}
344-
345380
result = result + str
346381
appendValue(&data, concatWithFirstRune(last, str))
347382
last = str
348383
}
349384
}
385+
386+
//func DecompressFromBase64New(input string) (string, error) {
387+
// data := dataStruct{input, getBaseValue(input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2}
388+
// result, isEnd, err := getString("", &data)
389+
// if err != nil || isEnd {
390+
// return result, err
391+
// }
392+
// last := result
393+
// data.numBits += 1
394+
// newResBuf := strings.Builder{}
395+
// for {
396+
// str, isEnd, err := getString(last, &data)
397+
// if err != nil || isEnd {
398+
// return newResBuf.String(), err
399+
// }
400+
// newResBuf.WriteString(str)
401+
// appendValue(&data, concatWithFirstRune(last, str))
402+
// last = str
403+
// }
404+
//}

0 commit comments

Comments
 (0)