1
// Copyright 2019+ Klaus Post. All rights reserved.
2
// License information can be found in the LICENSE file.
3
// Based on work by Yann Collet, released under BSD License.
10
betterLongTableBits = 19 // Bits used in the long match table
11
betterLongTableSize = 1 << betterLongTableBits // Size of the table
12
betterLongLen = 8 // Bytes used for table hash
14
// Note: Increasing the short table bits or making the hash shorter
15
// can actually lead to compression degradation since it will 'steal' more from the
16
// long match table and match offsets are quite big.
17
// This greatly depends on the type of input.
18
betterShortTableBits = 13 // Bits used in the short match table
19
betterShortTableSize = 1 << betterShortTableBits // Size of the table
20
betterShortLen = 5 // Bytes used for table hash
22
betterLongTableShardCnt = 1 << (betterLongTableBits - dictShardBits) // Number of shards in the table
23
betterLongTableShardSize = betterLongTableSize / betterLongTableShardCnt // Size of an individual shard
25
betterShortTableShardCnt = 1 << (betterShortTableBits - dictShardBits) // Number of shards in the table
26
betterShortTableShardSize = betterShortTableSize / betterShortTableShardCnt // Size of an individual shard
29
type prevEntry struct {
34
// betterFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches.
35
// The long match table contains the previous entry with the same hash,
36
// effectively making it a "chain" of length 2.
37
// When we find a long match we choose between the two values and select the longest.
38
// When we find a short match, after checking the long, we check if we can find a long at n+1
39
// and that it is longer (lazy matching).
40
type betterFastEncoder struct {
42
table [betterShortTableSize]tableEntry
43
longTable [betterLongTableSize]prevEntry
46
type betterFastEncoderDict struct {
48
dictTable []tableEntry
49
dictLongTable []prevEntry
50
shortTableShardDirty [betterShortTableShardCnt]bool
51
longTableShardDirty [betterLongTableShardCnt]bool
55
// Encode improves compression...
56
func (e *betterFastEncoder) Encode(blk *blockEnc, src []byte) {
58
// Input margin is the number of bytes we read (8)
59
// and the maximum we will read ahead (2)
61
minNonLiteralBlockSize = 16
64
// Protect against e.cur wraparound.
65
for e.cur >= bufferReset {
67
for i := range e.table[:] {
68
e.table[i] = tableEntry{}
70
for i := range e.longTable[:] {
71
e.longTable[i] = prevEntry{}
76
// Shift down everything in the table that isn't already too far away.
77
minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff
78
for i := range e.table[:] {
79
v := e.table[i].offset
83
v = v - e.cur + e.maxMatchOff
87
for i := range e.longTable[:] {
88
v := e.longTable[i].offset
89
v2 := e.longTable[i].prev
94
v = v - e.cur + e.maxMatchOff
98
v2 = v2 - e.cur + e.maxMatchOff
101
e.longTable[i] = prevEntry{
106
e.cur = e.maxMatchOff
112
if len(src) < minNonLiteralBlockSize {
113
blk.extraLits = len(src)
114
blk.literals = blk.literals[:len(src)]
115
copy(blk.literals, src)
121
sLimit := int32(len(src)) - inputMargin
122
// stepSize is the number of bytes to skip on every main loop iteration.
123
// It should be >= 1.
126
const kSearchStrength = 9
128
// nextEmit is where in src the next emitLiteral should start from.
130
cv := load6432(src, s)
133
offset1 := int32(blk.recentOffsets[0])
134
offset2 := int32(blk.recentOffsets[1])
136
addLiterals := func(s *seq, until int32) {
137
if until == nextEmit {
140
blk.literals = append(blk.literals, src[nextEmit:until]...)
141
s.litLen = uint32(until - nextEmit)
144
println("recent offsets:", blk.recentOffsets)
150
// We allow the encoder to optionally turn off repeat offsets across blocks
151
canRepeat := len(blk.sequences) > 2
155
if debugAsserts && canRepeat && offset1 == 0 {
156
panic("offset0 was 0")
159
nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
160
nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
161
candidateL := e.longTable[nextHashL]
162
candidateS := e.table[nextHashS]
165
repIndex := s - offset1 + repOff
167
e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset}
168
e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)}
171
if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) {
172
// Consider history as well.
174
lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src)
176
seq.matchLen = uint32(lenght - zstdMinMatch)
178
// We might be able to match backwards.
179
// Extend as long as we can.
181
// We end the search early, so we don't risk 0 literals
182
// and have to do special offset treatment.
183
startLimit := nextEmit + 1
185
tMin := s - e.maxMatchOff
189
for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
194
addLiterals(&seq, start)
199
println("repeat sequence", seq, "next s:", s)
201
blk.sequences = append(blk.sequences, seq)
203
// Index match start+1 (long) -> s - 1
210
println("repeat ended", s, lenght)
217
cv0 := load6432(src, index0)
219
h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
220
off := index0 + e.cur
221
e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
222
e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)}
225
cv = load6432(src, s)
230
// We deviate from the reference encoder and also check offset 2.
231
// Still slower and not much better, so disabled.
232
// repIndex = s - offset2 + repOff2
233
if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) {
234
// Consider history as well.
236
lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src)
238
seq.matchLen = uint32(lenght - zstdMinMatch)
240
// We might be able to match backwards.
241
// Extend as long as we can.
243
// We end the search early, so we don't risk 0 literals
244
// and have to do special offset treatment.
245
startLimit := nextEmit + 1
247
tMin := s - e.maxMatchOff
251
for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
256
addLiterals(&seq, start)
261
println("repeat sequence 2", seq, "next s:", s)
263
blk.sequences = append(blk.sequences, seq)
265
index0 := s + repOff2
266
s += lenght + repOff2
270
println("repeat ended", s, lenght)
278
cv0 := load6432(src, index0)
280
h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
281
off := index0 + e.cur
282
e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
283
e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)}
286
cv = load6432(src, s)
288
offset1, offset2 = offset2, offset1
292
// Find the offsets of our two matches.
293
coffsetL := candidateL.offset - e.cur
294
coffsetLP := candidateL.prev - e.cur
296
// Check if we have a long match.
297
if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
298
// Found a long match, at least 8 bytes.
299
matched = e.matchlen(s+8, coffsetL+8, src) + 8
301
if debugAsserts && s <= t {
302
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
304
if debugAsserts && s-t > e.maxMatchOff {
305
panic("s - t >e.maxMatchOff")
308
println("long match")
311
if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
312
// Found a long match, at least 8 bytes.
313
prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8
314
if prevMatch > matched {
318
if debugAsserts && s <= t {
319
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
321
if debugAsserts && s-t > e.maxMatchOff {
322
panic("s - t >e.maxMatchOff")
325
println("long match")
331
// Check if we have a long match on prev.
332
if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
333
// Found a long match, at least 8 bytes.
334
matched = e.matchlen(s+8, coffsetLP+8, src) + 8
336
if debugAsserts && s <= t {
337
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
339
if debugAsserts && s-t > e.maxMatchOff {
340
panic("s - t >e.maxMatchOff")
343
println("long match")
348
coffsetS := candidateS.offset - e.cur
350
// Check if we have a short match.
351
if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val {
352
// found a regular match
353
matched = e.matchlen(s+4, coffsetS+4, src) + 4
355
// See if we can find a long match at s+1
357
cv := load6432(src, s+checkAt)
358
nextHashL = hashLen(cv, betterLongTableBits, betterLongLen)
359
candidateL = e.longTable[nextHashL]
360
coffsetL = candidateL.offset - e.cur
362
// We can store it, since we have at least a 4 byte match.
363
e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset}
364
if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
365
// Found a long match, at least 8 bytes.
366
matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
367
if matchedNext > matched {
370
matched = matchedNext
372
println("long match (after short)")
378
// Check prev long...
379
coffsetL = candidateL.prev - e.cur
380
if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
381
// Found a long match, at least 8 bytes.
382
matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
383
if matchedNext > matched {
386
matched = matchedNext
388
println("prev long match (after short)")
394
if debugAsserts && s <= t {
395
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
397
if debugAsserts && s-t > e.maxMatchOff {
398
panic("s - t >e.maxMatchOff")
400
if debugAsserts && t < 0 {
404
println("short match")
409
// No match found, move forward in input.
410
s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1))
414
cv = load6432(src, s)
417
// Try to find a better match by searching for a long match at the end of the current best match
418
if s+matched < sLimit {
419
nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen)
420
cv := load3232(src, s)
421
candidateL := e.longTable[nextHashL]
422
coffsetL := candidateL.offset - e.cur - matched
423
if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
424
// Found a long match, at least 4 bytes.
425
matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4
426
if matchedNext > matched {
428
matched = matchedNext
430
println("long match at end-of-match")
435
// Check prev long...
437
coffsetL = candidateL.prev - e.cur - matched
438
if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
439
// Found a long match, at least 4 bytes.
440
matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4
441
if matchedNext > matched {
443
matched = matchedNext
445
println("prev long match at end-of-match")
451
// A match has been found. Update recent offsets.
455
if debugAsserts && s <= t {
456
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
459
if debugAsserts && canRepeat && int(offset1) > len(src) {
460
panic("invalid offset")
463
// Extend the n-byte match as long as possible.
467
tMin := s - e.maxMatchOff
471
for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength {
477
// Write our sequence
479
seq.litLen = uint32(s - nextEmit)
480
seq.matchLen = uint32(l - zstdMinMatch)
482
blk.literals = append(blk.literals, src[nextEmit:s]...)
484
seq.offset = uint32(s-t) + 3
487
println("sequence", seq, "next s:", s)
489
blk.sequences = append(blk.sequences, seq)
495
// Index match start+1 (long) -> s - 1
498
cv0 := load6432(src, index0)
500
h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
501
off := index0 + e.cur
502
e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
503
e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)}
507
cv = load6432(src, s)
515
if load3232(src, o2) != uint32(cv) {
520
// Store this, since we have it.
521
nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
522
nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
524
// We have at least 4 byte match.
525
// No need to check backwards. We come straight from a match
526
l := 4 + e.matchlen(s+4, o2+4, src)
528
e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset}
529
e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)}
530
seq.matchLen = uint32(l) - zstdMinMatch
533
// Since litlen is always 0, this is offset 1.
538
println("sequence", seq, "next s:", s)
540
blk.sequences = append(blk.sequences, seq)
542
// Swap offset 1 and 2.
543
offset1, offset2 = offset2, offset1
548
cv = load6432(src, s)
552
if int(nextEmit) < len(src) {
553
blk.literals = append(blk.literals, src[nextEmit:]...)
554
blk.extraLits = len(src) - int(nextEmit)
556
blk.recentOffsets[0] = uint32(offset1)
557
blk.recentOffsets[1] = uint32(offset2)
559
println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits)
563
// EncodeNoHist will encode a block with no history and no following blocks.
564
// Most notable difference is that src will not be copied for history and
565
// we do not need to check for max match length.
566
func (e *betterFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) {
567
e.ensureHist(len(src))
571
// Encode improves compression...
572
func (e *betterFastEncoderDict) Encode(blk *blockEnc, src []byte) {
574
// Input margin is the number of bytes we read (8)
575
// and the maximum we will read ahead (2)
577
minNonLiteralBlockSize = 16
580
// Protect against e.cur wraparound.
581
for e.cur >= bufferReset {
582
if len(e.hist) == 0 {
583
for i := range e.table[:] {
584
e.table[i] = tableEntry{}
586
for i := range e.longTable[:] {
587
e.longTable[i] = prevEntry{}
589
e.cur = e.maxMatchOff
593
// Shift down everything in the table that isn't already too far away.
594
minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff
595
for i := range e.table[:] {
596
v := e.table[i].offset
600
v = v - e.cur + e.maxMatchOff
602
e.table[i].offset = v
604
for i := range e.longTable[:] {
605
v := e.longTable[i].offset
606
v2 := e.longTable[i].prev
611
v = v - e.cur + e.maxMatchOff
615
v2 = v2 - e.cur + e.maxMatchOff
618
e.longTable[i] = prevEntry{
624
e.cur = e.maxMatchOff
630
if len(src) < minNonLiteralBlockSize {
631
blk.extraLits = len(src)
632
blk.literals = blk.literals[:len(src)]
633
copy(blk.literals, src)
639
sLimit := int32(len(src)) - inputMargin
640
// stepSize is the number of bytes to skip on every main loop iteration.
641
// It should be >= 1.
644
const kSearchStrength = 9
646
// nextEmit is where in src the next emitLiteral should start from.
648
cv := load6432(src, s)
651
offset1 := int32(blk.recentOffsets[0])
652
offset2 := int32(blk.recentOffsets[1])
654
addLiterals := func(s *seq, until int32) {
655
if until == nextEmit {
658
blk.literals = append(blk.literals, src[nextEmit:until]...)
659
s.litLen = uint32(until - nextEmit)
662
println("recent offsets:", blk.recentOffsets)
668
// We allow the encoder to optionally turn off repeat offsets across blocks
669
canRepeat := len(blk.sequences) > 2
673
if debugAsserts && canRepeat && offset1 == 0 {
674
panic("offset0 was 0")
677
nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
678
nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
679
candidateL := e.longTable[nextHashL]
680
candidateS := e.table[nextHashS]
683
repIndex := s - offset1 + repOff
685
e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset}
686
e.markLongShardDirty(nextHashL)
687
e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)}
688
e.markShortShardDirty(nextHashS)
691
if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) {
692
// Consider history as well.
694
lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src)
696
seq.matchLen = uint32(lenght - zstdMinMatch)
698
// We might be able to match backwards.
699
// Extend as long as we can.
701
// We end the search early, so we don't risk 0 literals
702
// and have to do special offset treatment.
703
startLimit := nextEmit + 1
705
tMin := s - e.maxMatchOff
709
for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
714
addLiterals(&seq, start)
719
println("repeat sequence", seq, "next s:", s)
721
blk.sequences = append(blk.sequences, seq)
723
// Index match start+1 (long) -> s - 1
730
println("repeat ended", s, lenght)
737
cv0 := load6432(src, index0)
739
h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
740
off := index0 + e.cur
741
e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
742
e.markLongShardDirty(h0)
743
h1 := hashLen(cv1, betterShortTableBits, betterShortLen)
744
e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)}
745
e.markShortShardDirty(h1)
748
cv = load6432(src, s)
753
// We deviate from the reference encoder and also check offset 2.
754
// Still slower and not much better, so disabled.
755
// repIndex = s - offset2 + repOff2
756
if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) {
757
// Consider history as well.
759
lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src)
761
seq.matchLen = uint32(lenght - zstdMinMatch)
763
// We might be able to match backwards.
764
// Extend as long as we can.
766
// We end the search early, so we don't risk 0 literals
767
// and have to do special offset treatment.
768
startLimit := nextEmit + 1
770
tMin := s - e.maxMatchOff
774
for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
779
addLiterals(&seq, start)
784
println("repeat sequence 2", seq, "next s:", s)
786
blk.sequences = append(blk.sequences, seq)
788
index0 := s + repOff2
789
s += lenght + repOff2
793
println("repeat ended", s, lenght)
801
cv0 := load6432(src, index0)
803
h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
804
off := index0 + e.cur
805
e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
806
e.markLongShardDirty(h0)
807
h1 := hashLen(cv1, betterShortTableBits, betterShortLen)
808
e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)}
809
e.markShortShardDirty(h1)
812
cv = load6432(src, s)
814
offset1, offset2 = offset2, offset1
818
// Find the offsets of our two matches.
819
coffsetL := candidateL.offset - e.cur
820
coffsetLP := candidateL.prev - e.cur
822
// Check if we have a long match.
823
if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
824
// Found a long match, at least 8 bytes.
825
matched = e.matchlen(s+8, coffsetL+8, src) + 8
827
if debugAsserts && s <= t {
828
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
830
if debugAsserts && s-t > e.maxMatchOff {
831
panic("s - t >e.maxMatchOff")
834
println("long match")
837
if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
838
// Found a long match, at least 8 bytes.
839
prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8
840
if prevMatch > matched {
844
if debugAsserts && s <= t {
845
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
847
if debugAsserts && s-t > e.maxMatchOff {
848
panic("s - t >e.maxMatchOff")
851
println("long match")
857
// Check if we have a long match on prev.
858
if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
859
// Found a long match, at least 8 bytes.
860
matched = e.matchlen(s+8, coffsetLP+8, src) + 8
862
if debugAsserts && s <= t {
863
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
865
if debugAsserts && s-t > e.maxMatchOff {
866
panic("s - t >e.maxMatchOff")
869
println("long match")
874
coffsetS := candidateS.offset - e.cur
876
// Check if we have a short match.
877
if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val {
878
// found a regular match
879
matched = e.matchlen(s+4, coffsetS+4, src) + 4
881
// See if we can find a long match at s+1
883
cv := load6432(src, s+checkAt)
884
nextHashL = hashLen(cv, betterLongTableBits, betterLongLen)
885
candidateL = e.longTable[nextHashL]
886
coffsetL = candidateL.offset - e.cur
888
// We can store it, since we have at least a 4 byte match.
889
e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset}
890
e.markLongShardDirty(nextHashL)
891
if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
892
// Found a long match, at least 8 bytes.
893
matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
894
if matchedNext > matched {
897
matched = matchedNext
899
println("long match (after short)")
905
// Check prev long...
906
coffsetL = candidateL.prev - e.cur
907
if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
908
// Found a long match, at least 8 bytes.
909
matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
910
if matchedNext > matched {
913
matched = matchedNext
915
println("prev long match (after short)")
921
if debugAsserts && s <= t {
922
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
924
if debugAsserts && s-t > e.maxMatchOff {
925
panic("s - t >e.maxMatchOff")
927
if debugAsserts && t < 0 {
931
println("short match")
936
// No match found, move forward in input.
937
s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1))
941
cv = load6432(src, s)
943
// Try to find a better match by searching for a long match at the end of the current best match
944
if s+matched < sLimit {
945
nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen)
946
cv := load3232(src, s)
947
candidateL := e.longTable[nextHashL]
948
coffsetL := candidateL.offset - e.cur - matched
949
if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
950
// Found a long match, at least 4 bytes.
951
matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4
952
if matchedNext > matched {
954
matched = matchedNext
956
println("long match at end-of-match")
961
// Check prev long...
963
coffsetL = candidateL.prev - e.cur - matched
964
if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
965
// Found a long match, at least 4 bytes.
966
matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4
967
if matchedNext > matched {
969
matched = matchedNext
971
println("prev long match at end-of-match")
977
// A match has been found. Update recent offsets.
981
if debugAsserts && s <= t {
982
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
985
if debugAsserts && canRepeat && int(offset1) > len(src) {
986
panic("invalid offset")
989
// Extend the n-byte match as long as possible.
993
tMin := s - e.maxMatchOff
997
for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength {
1003
// Write our sequence
1005
seq.litLen = uint32(s - nextEmit)
1006
seq.matchLen = uint32(l - zstdMinMatch)
1008
blk.literals = append(blk.literals, src[nextEmit:s]...)
1010
seq.offset = uint32(s-t) + 3
1013
println("sequence", seq, "next s:", s)
1015
blk.sequences = append(blk.sequences, seq)
1021
// Index match start+1 (long) -> s - 1
1024
cv0 := load6432(src, index0)
1026
h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
1027
off := index0 + e.cur
1028
e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
1029
e.markLongShardDirty(h0)
1030
h1 := hashLen(cv1, betterShortTableBits, betterShortLen)
1031
e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)}
1032
e.markShortShardDirty(h1)
1036
cv = load6432(src, s)
1044
if load3232(src, o2) != uint32(cv) {
1045
// Do regular search
1049
// Store this, since we have it.
1050
nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
1051
nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
1053
// We have at least 4 byte match.
1054
// No need to check backwards. We come straight from a match
1055
l := 4 + e.matchlen(s+4, o2+4, src)
1057
e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset}
1058
e.markLongShardDirty(nextHashL)
1059
e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)}
1060
e.markShortShardDirty(nextHashS)
1061
seq.matchLen = uint32(l) - zstdMinMatch
1064
// Since litlen is always 0, this is offset 1.
1069
println("sequence", seq, "next s:", s)
1071
blk.sequences = append(blk.sequences, seq)
1073
// Swap offset 1 and 2.
1074
offset1, offset2 = offset2, offset1
1079
cv = load6432(src, s)
1083
if int(nextEmit) < len(src) {
1084
blk.literals = append(blk.literals, src[nextEmit:]...)
1085
blk.extraLits = len(src) - int(nextEmit)
1087
blk.recentOffsets[0] = uint32(offset1)
1088
blk.recentOffsets[1] = uint32(offset2)
1090
println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits)
1094
// ResetDict will reset and set a dictionary if not nil
1095
func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) {
1096
e.resetBase(d, singleBlock)
1098
panic("betterFastEncoder: Reset with dict")
1102
// ResetDict will reset and set a dictionary if not nil
1103
func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) {
1104
e.resetBase(d, singleBlock)
1108
// Init or copy dict table
1109
if len(e.dictTable) != len(e.table) || d.id != e.lastDictID {
1110
if len(e.dictTable) != len(e.table) {
1111
e.dictTable = make([]tableEntry, len(e.table))
1113
end := int32(len(d.content)) - 8 + e.maxMatchOff
1114
for i := e.maxMatchOff; i < end; i += 4 {
1115
const hashLog = betterShortTableBits
1117
cv := load6432(d.content, i-e.maxMatchOff)
1118
nextHash := hashLen(cv, hashLog, betterShortLen) // 0 -> 4
1119
nextHash1 := hashLen(cv>>8, hashLog, betterShortLen) // 1 -> 5
1120
nextHash2 := hashLen(cv>>16, hashLog, betterShortLen) // 2 -> 6
1121
nextHash3 := hashLen(cv>>24, hashLog, betterShortLen) // 3 -> 7
1122
e.dictTable[nextHash] = tableEntry{
1126
e.dictTable[nextHash1] = tableEntry{
1127
val: uint32(cv >> 8),
1130
e.dictTable[nextHash2] = tableEntry{
1131
val: uint32(cv >> 16),
1134
e.dictTable[nextHash3] = tableEntry{
1135
val: uint32(cv >> 24),
1143
// Init or copy dict table
1144
if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID {
1145
if len(e.dictLongTable) != len(e.longTable) {
1146
e.dictLongTable = make([]prevEntry, len(e.longTable))
1148
if len(d.content) >= 8 {
1149
cv := load6432(d.content, 0)
1150
h := hashLen(cv, betterLongTableBits, betterLongLen)
1151
e.dictLongTable[h] = prevEntry{
1152
offset: e.maxMatchOff,
1153
prev: e.dictLongTable[h].offset,
1156
end := int32(len(d.content)) - 8 + e.maxMatchOff
1157
off := 8 // First to read
1158
for i := e.maxMatchOff + 1; i < end; i++ {
1159
cv = cv>>8 | (uint64(d.content[off]) << 56)
1160
h := hashLen(cv, betterLongTableBits, betterLongLen)
1161
e.dictLongTable[h] = prevEntry{
1163
prev: e.dictLongTable[h].offset,
1172
// Reset table to initial state
1176
for i := range e.shortTableShardDirty {
1177
if e.shortTableShardDirty[i] {
1182
const shardCnt = betterShortTableShardCnt
1183
const shardSize = betterShortTableShardSize
1184
if e.allDirty || dirtyShardCnt > shardCnt*4/6 {
1185
copy(e.table[:], e.dictTable)
1186
for i := range e.shortTableShardDirty {
1187
e.shortTableShardDirty[i] = false
1190
for i := range e.shortTableShardDirty {
1191
if !e.shortTableShardDirty[i] {
1195
copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize])
1196
e.shortTableShardDirty[i] = false
1203
for i := range e.shortTableShardDirty {
1204
if e.shortTableShardDirty[i] {
1209
const shardCnt = betterLongTableShardCnt
1210
const shardSize = betterLongTableShardSize
1211
if e.allDirty || dirtyShardCnt > shardCnt*4/6 {
1212
copy(e.longTable[:], e.dictLongTable)
1213
for i := range e.longTableShardDirty {
1214
e.longTableShardDirty[i] = false
1217
for i := range e.longTableShardDirty {
1218
if !e.longTableShardDirty[i] {
1222
copy(e.longTable[i*shardSize:(i+1)*shardSize], e.dictLongTable[i*shardSize:(i+1)*shardSize])
1223
e.longTableShardDirty[i] = false
1227
e.cur = e.maxMatchOff
1231
func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) {
1232
e.longTableShardDirty[entryNum/betterLongTableShardSize] = true
1235
func (e *betterFastEncoderDict) markShortShardDirty(entryNum uint32) {
1236
e.shortTableShardDirty[entryNum/betterShortTableShardSize] = true