blob: 84a79fde76775b95adc9e83e629736e853990470 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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.
4
5package zstd
6
7import "fmt"
8
9const (
10 betterLongTableBits = 19 // Bits used in the long match table
11 betterLongTableSize = 1 << betterLongTableBits // Size of the table
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +053012 betterLongLen = 8 // Bytes used for table hash
khenaidood948f772021-08-11 17:49:24 -040013
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
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +053020 betterShortLen = 5 // Bytes used for table hash
khenaidood948f772021-08-11 17:49:24 -040021
22 betterLongTableShardCnt = 1 << (betterLongTableBits - dictShardBits) // Number of shards in the table
23 betterLongTableShardSize = betterLongTableSize / betterLongTableShardCnt // Size of an individual shard
24
25 betterShortTableShardCnt = 1 << (betterShortTableBits - dictShardBits) // Number of shards in the table
26 betterShortTableShardSize = betterShortTableSize / betterShortTableShardCnt // Size of an individual shard
27)
28
29type prevEntry struct {
30 offset int32
31 prev int32
32}
33
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).
40type betterFastEncoder struct {
41 fastBase
42 table [betterShortTableSize]tableEntry
43 longTable [betterLongTableSize]prevEntry
44}
45
46type betterFastEncoderDict struct {
47 betterFastEncoder
48 dictTable []tableEntry
49 dictLongTable []prevEntry
50 shortTableShardDirty [betterShortTableShardCnt]bool
51 longTableShardDirty [betterLongTableShardCnt]bool
52 allDirty bool
53}
54
55// Encode improves compression...
56func (e *betterFastEncoder) Encode(blk *blockEnc, src []byte) {
57 const (
58 // Input margin is the number of bytes we read (8)
59 // and the maximum we will read ahead (2)
60 inputMargin = 8 + 2
61 minNonLiteralBlockSize = 16
62 )
63
64 // Protect against e.cur wraparound.
Abhay Kumara2ae5992025-11-10 14:02:24 +000065 for e.cur >= e.bufferReset-int32(len(e.hist)) {
khenaidood948f772021-08-11 17:49:24 -040066 if len(e.hist) == 0 {
Abhay Kumara2ae5992025-11-10 14:02:24 +000067 e.table = [betterShortTableSize]tableEntry{}
68 e.longTable = [betterLongTableSize]prevEntry{}
khenaidood948f772021-08-11 17:49:24 -040069 e.cur = e.maxMatchOff
70 break
71 }
72 // Shift down everything in the table that isn't already too far away.
73 minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff
74 for i := range e.table[:] {
75 v := e.table[i].offset
76 if v < minOff {
77 v = 0
78 } else {
79 v = v - e.cur + e.maxMatchOff
80 }
81 e.table[i].offset = v
82 }
83 for i := range e.longTable[:] {
84 v := e.longTable[i].offset
85 v2 := e.longTable[i].prev
86 if v < minOff {
87 v = 0
88 v2 = 0
89 } else {
90 v = v - e.cur + e.maxMatchOff
91 if v2 < minOff {
92 v2 = 0
93 } else {
94 v2 = v2 - e.cur + e.maxMatchOff
95 }
96 }
97 e.longTable[i] = prevEntry{
98 offset: v,
99 prev: v2,
100 }
101 }
102 e.cur = e.maxMatchOff
103 break
104 }
Abhay Kumara2ae5992025-11-10 14:02:24 +0000105 // Add block to history
khenaidood948f772021-08-11 17:49:24 -0400106 s := e.addBlock(src)
107 blk.size = len(src)
Abhay Kumara2ae5992025-11-10 14:02:24 +0000108
109 // Check RLE first
110 if len(src) > zstdMinMatch {
111 ml := matchLen(src[1:], src)
112 if ml == len(src)-1 {
113 blk.literals = append(blk.literals, src[0])
114 blk.sequences = append(blk.sequences, seq{litLen: 1, matchLen: uint32(len(src)-1) - zstdMinMatch, offset: 1 + 3})
115 return
116 }
117 }
118
khenaidood948f772021-08-11 17:49:24 -0400119 if len(src) < minNonLiteralBlockSize {
120 blk.extraLits = len(src)
121 blk.literals = blk.literals[:len(src)]
122 copy(blk.literals, src)
123 return
124 }
125
126 // Override src
127 src = e.hist
128 sLimit := int32(len(src)) - inputMargin
129 // stepSize is the number of bytes to skip on every main loop iteration.
130 // It should be >= 1.
131 const stepSize = 1
132
133 const kSearchStrength = 9
134
135 // nextEmit is where in src the next emitLiteral should start from.
136 nextEmit := s
137 cv := load6432(src, s)
138
139 // Relative offsets
140 offset1 := int32(blk.recentOffsets[0])
141 offset2 := int32(blk.recentOffsets[1])
142
143 addLiterals := func(s *seq, until int32) {
144 if until == nextEmit {
145 return
146 }
147 blk.literals = append(blk.literals, src[nextEmit:until]...)
148 s.litLen = uint32(until - nextEmit)
149 }
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530150 if debugEncoder {
khenaidood948f772021-08-11 17:49:24 -0400151 println("recent offsets:", blk.recentOffsets)
152 }
153
154encodeLoop:
155 for {
156 var t int32
157 // We allow the encoder to optionally turn off repeat offsets across blocks
158 canRepeat := len(blk.sequences) > 2
Abhay Kumara2ae5992025-11-10 14:02:24 +0000159 var matched, index0 int32
khenaidood948f772021-08-11 17:49:24 -0400160
161 for {
162 if debugAsserts && canRepeat && offset1 == 0 {
163 panic("offset0 was 0")
164 }
165
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530166 nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
167 nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -0400168 candidateL := e.longTable[nextHashL]
169 candidateS := e.table[nextHashS]
170
171 const repOff = 1
172 repIndex := s - offset1 + repOff
173 off := s + e.cur
174 e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset}
175 e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)}
Abhay Kumara2ae5992025-11-10 14:02:24 +0000176 index0 = s + 1
khenaidood948f772021-08-11 17:49:24 -0400177
178 if canRepeat {
179 if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) {
180 // Consider history as well.
181 var seq seq
Abhay Kumara2ae5992025-11-10 14:02:24 +0000182 length := 4 + e.matchlen(s+4+repOff, repIndex+4, src)
khenaidood948f772021-08-11 17:49:24 -0400183
Abhay Kumara2ae5992025-11-10 14:02:24 +0000184 seq.matchLen = uint32(length - zstdMinMatch)
khenaidood948f772021-08-11 17:49:24 -0400185
186 // We might be able to match backwards.
187 // Extend as long as we can.
188 start := s + repOff
189 // We end the search early, so we don't risk 0 literals
190 // and have to do special offset treatment.
191 startLimit := nextEmit + 1
192
193 tMin := s - e.maxMatchOff
194 if tMin < 0 {
195 tMin = 0
196 }
197 for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
198 repIndex--
199 start--
200 seq.matchLen++
201 }
202 addLiterals(&seq, start)
203
204 // rep 0
205 seq.offset = 1
206 if debugSequences {
207 println("repeat sequence", seq, "next s:", s)
208 }
209 blk.sequences = append(blk.sequences, seq)
210
211 // Index match start+1 (long) -> s - 1
212 index0 := s + repOff
Abhay Kumara2ae5992025-11-10 14:02:24 +0000213 s += length + repOff
khenaidood948f772021-08-11 17:49:24 -0400214
215 nextEmit = s
216 if s >= sLimit {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530217 if debugEncoder {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000218 println("repeat ended", s, length)
khenaidood948f772021-08-11 17:49:24 -0400219
220 }
221 break encodeLoop
222 }
223 // Index skipped...
224 for index0 < s-1 {
225 cv0 := load6432(src, index0)
226 cv1 := cv0 >> 8
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530227 h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400228 off := index0 + e.cur
229 e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530230 e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)}
khenaidood948f772021-08-11 17:49:24 -0400231 index0 += 2
232 }
233 cv = load6432(src, s)
234 continue
235 }
236 const repOff2 = 1
237
238 // We deviate from the reference encoder and also check offset 2.
239 // Still slower and not much better, so disabled.
240 // repIndex = s - offset2 + repOff2
241 if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) {
242 // Consider history as well.
243 var seq seq
Abhay Kumara2ae5992025-11-10 14:02:24 +0000244 length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src)
khenaidood948f772021-08-11 17:49:24 -0400245
Abhay Kumara2ae5992025-11-10 14:02:24 +0000246 seq.matchLen = uint32(length - zstdMinMatch)
khenaidood948f772021-08-11 17:49:24 -0400247
248 // We might be able to match backwards.
249 // Extend as long as we can.
250 start := s + repOff2
251 // We end the search early, so we don't risk 0 literals
252 // and have to do special offset treatment.
253 startLimit := nextEmit + 1
254
255 tMin := s - e.maxMatchOff
256 if tMin < 0 {
257 tMin = 0
258 }
259 for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
260 repIndex--
261 start--
262 seq.matchLen++
263 }
264 addLiterals(&seq, start)
265
266 // rep 2
267 seq.offset = 2
268 if debugSequences {
269 println("repeat sequence 2", seq, "next s:", s)
270 }
271 blk.sequences = append(blk.sequences, seq)
272
Abhay Kumara2ae5992025-11-10 14:02:24 +0000273 s += length + repOff2
khenaidood948f772021-08-11 17:49:24 -0400274 nextEmit = s
275 if s >= sLimit {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530276 if debugEncoder {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000277 println("repeat ended", s, length)
khenaidood948f772021-08-11 17:49:24 -0400278
279 }
280 break encodeLoop
281 }
282
283 // Index skipped...
284 for index0 < s-1 {
285 cv0 := load6432(src, index0)
286 cv1 := cv0 >> 8
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530287 h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400288 off := index0 + e.cur
289 e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530290 e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)}
khenaidood948f772021-08-11 17:49:24 -0400291 index0 += 2
292 }
293 cv = load6432(src, s)
294 // Swap offsets
295 offset1, offset2 = offset2, offset1
296 continue
297 }
298 }
299 // Find the offsets of our two matches.
300 coffsetL := candidateL.offset - e.cur
301 coffsetLP := candidateL.prev - e.cur
302
303 // Check if we have a long match.
304 if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
305 // Found a long match, at least 8 bytes.
306 matched = e.matchlen(s+8, coffsetL+8, src) + 8
307 t = coffsetL
308 if debugAsserts && s <= t {
309 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
310 }
311 if debugAsserts && s-t > e.maxMatchOff {
312 panic("s - t >e.maxMatchOff")
313 }
314 if debugMatches {
315 println("long match")
316 }
317
318 if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
319 // Found a long match, at least 8 bytes.
320 prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8
321 if prevMatch > matched {
322 matched = prevMatch
323 t = coffsetLP
324 }
325 if debugAsserts && s <= t {
326 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
327 }
328 if debugAsserts && s-t > e.maxMatchOff {
329 panic("s - t >e.maxMatchOff")
330 }
331 if debugMatches {
332 println("long match")
333 }
334 }
335 break
336 }
337
338 // Check if we have a long match on prev.
339 if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
340 // Found a long match, at least 8 bytes.
341 matched = e.matchlen(s+8, coffsetLP+8, src) + 8
342 t = coffsetLP
343 if debugAsserts && s <= t {
344 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
345 }
346 if debugAsserts && s-t > e.maxMatchOff {
347 panic("s - t >e.maxMatchOff")
348 }
349 if debugMatches {
350 println("long match")
351 }
352 break
353 }
354
355 coffsetS := candidateS.offset - e.cur
356
357 // Check if we have a short match.
358 if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val {
359 // found a regular match
360 matched = e.matchlen(s+4, coffsetS+4, src) + 4
361
362 // See if we can find a long match at s+1
363 const checkAt = 1
364 cv := load6432(src, s+checkAt)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530365 nextHashL = hashLen(cv, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400366 candidateL = e.longTable[nextHashL]
367 coffsetL = candidateL.offset - e.cur
368
369 // We can store it, since we have at least a 4 byte match.
370 e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset}
371 if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
372 // Found a long match, at least 8 bytes.
373 matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
374 if matchedNext > matched {
375 t = coffsetL
376 s += checkAt
377 matched = matchedNext
378 if debugMatches {
379 println("long match (after short)")
380 }
381 break
382 }
383 }
384
385 // Check prev long...
386 coffsetL = candidateL.prev - e.cur
387 if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
388 // Found a long match, at least 8 bytes.
389 matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
390 if matchedNext > matched {
391 t = coffsetL
392 s += checkAt
393 matched = matchedNext
394 if debugMatches {
395 println("prev long match (after short)")
396 }
397 break
398 }
399 }
400 t = coffsetS
401 if debugAsserts && s <= t {
402 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
403 }
404 if debugAsserts && s-t > e.maxMatchOff {
405 panic("s - t >e.maxMatchOff")
406 }
407 if debugAsserts && t < 0 {
408 panic("t<0")
409 }
410 if debugMatches {
411 println("short match")
412 }
413 break
414 }
415
416 // No match found, move forward in input.
417 s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1))
418 if s >= sLimit {
419 break encodeLoop
420 }
421 cv = load6432(src, s)
422 }
423
424 // Try to find a better match by searching for a long match at the end of the current best match
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530425 if s+matched < sLimit {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000426 // Allow some bytes at the beginning to mismatch.
427 // Sweet spot is around 3 bytes, but depends on input.
428 // The skipped bytes are tested in Extend backwards,
429 // and still picked up as part of the match if they do.
430 const skipBeginning = 3
431
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530432 nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen)
Abhay Kumara2ae5992025-11-10 14:02:24 +0000433 s2 := s + skipBeginning
434 cv := load3232(src, s2)
khenaidood948f772021-08-11 17:49:24 -0400435 candidateL := e.longTable[nextHashL]
Abhay Kumara2ae5992025-11-10 14:02:24 +0000436 coffsetL := candidateL.offset - e.cur - matched + skipBeginning
437 if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
khenaidood948f772021-08-11 17:49:24 -0400438 // Found a long match, at least 4 bytes.
Abhay Kumara2ae5992025-11-10 14:02:24 +0000439 matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4
khenaidood948f772021-08-11 17:49:24 -0400440 if matchedNext > matched {
441 t = coffsetL
Abhay Kumara2ae5992025-11-10 14:02:24 +0000442 s = s2
khenaidood948f772021-08-11 17:49:24 -0400443 matched = matchedNext
444 if debugMatches {
445 println("long match at end-of-match")
446 }
447 }
448 }
449
450 // Check prev long...
451 if true {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000452 coffsetL = candidateL.prev - e.cur - matched + skipBeginning
453 if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
khenaidood948f772021-08-11 17:49:24 -0400454 // Found a long match, at least 4 bytes.
Abhay Kumara2ae5992025-11-10 14:02:24 +0000455 matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4
khenaidood948f772021-08-11 17:49:24 -0400456 if matchedNext > matched {
457 t = coffsetL
Abhay Kumara2ae5992025-11-10 14:02:24 +0000458 s = s2
khenaidood948f772021-08-11 17:49:24 -0400459 matched = matchedNext
460 if debugMatches {
461 println("prev long match at end-of-match")
462 }
463 }
464 }
465 }
466 }
467 // A match has been found. Update recent offsets.
468 offset2 = offset1
469 offset1 = s - t
470
471 if debugAsserts && s <= t {
472 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
473 }
474
475 if debugAsserts && canRepeat && int(offset1) > len(src) {
476 panic("invalid offset")
477 }
478
479 // Extend the n-byte match as long as possible.
480 l := matched
481
482 // Extend backwards
483 tMin := s - e.maxMatchOff
484 if tMin < 0 {
485 tMin = 0
486 }
487 for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength {
488 s--
489 t--
490 l++
491 }
492
493 // Write our sequence
494 var seq seq
495 seq.litLen = uint32(s - nextEmit)
496 seq.matchLen = uint32(l - zstdMinMatch)
497 if seq.litLen > 0 {
498 blk.literals = append(blk.literals, src[nextEmit:s]...)
499 }
500 seq.offset = uint32(s-t) + 3
501 s += l
502 if debugSequences {
503 println("sequence", seq, "next s:", s)
504 }
505 blk.sequences = append(blk.sequences, seq)
506 nextEmit = s
507 if s >= sLimit {
508 break encodeLoop
509 }
510
511 // Index match start+1 (long) -> s - 1
Abhay Kumara2ae5992025-11-10 14:02:24 +0000512 off := index0 + e.cur
khenaidood948f772021-08-11 17:49:24 -0400513 for index0 < s-1 {
514 cv0 := load6432(src, index0)
515 cv1 := cv0 >> 8
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530516 h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400517 e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530518 e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)}
khenaidood948f772021-08-11 17:49:24 -0400519 index0 += 2
Abhay Kumara2ae5992025-11-10 14:02:24 +0000520 off += 2
khenaidood948f772021-08-11 17:49:24 -0400521 }
522
523 cv = load6432(src, s)
524 if !canRepeat {
525 continue
526 }
527
528 // Check offset 2
529 for {
530 o2 := s - offset2
531 if load3232(src, o2) != uint32(cv) {
532 // Do regular search
533 break
534 }
535
536 // Store this, since we have it.
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530537 nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
538 nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -0400539
540 // We have at least 4 byte match.
541 // No need to check backwards. We come straight from a match
542 l := 4 + e.matchlen(s+4, o2+4, src)
543
544 e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset}
545 e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)}
546 seq.matchLen = uint32(l) - zstdMinMatch
547 seq.litLen = 0
548
549 // Since litlen is always 0, this is offset 1.
550 seq.offset = 1
551 s += l
552 nextEmit = s
553 if debugSequences {
554 println("sequence", seq, "next s:", s)
555 }
556 blk.sequences = append(blk.sequences, seq)
557
558 // Swap offset 1 and 2.
559 offset1, offset2 = offset2, offset1
560 if s >= sLimit {
561 // Finished
562 break encodeLoop
563 }
564 cv = load6432(src, s)
565 }
566 }
567
568 if int(nextEmit) < len(src) {
569 blk.literals = append(blk.literals, src[nextEmit:]...)
570 blk.extraLits = len(src) - int(nextEmit)
571 }
572 blk.recentOffsets[0] = uint32(offset1)
573 blk.recentOffsets[1] = uint32(offset2)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530574 if debugEncoder {
khenaidood948f772021-08-11 17:49:24 -0400575 println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits)
576 }
577}
578
579// EncodeNoHist will encode a block with no history and no following blocks.
580// Most notable difference is that src will not be copied for history and
581// we do not need to check for max match length.
582func (e *betterFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) {
583 e.ensureHist(len(src))
584 e.Encode(blk, src)
585}
586
587// Encode improves compression...
588func (e *betterFastEncoderDict) Encode(blk *blockEnc, src []byte) {
589 const (
590 // Input margin is the number of bytes we read (8)
591 // and the maximum we will read ahead (2)
592 inputMargin = 8 + 2
593 minNonLiteralBlockSize = 16
594 )
595
596 // Protect against e.cur wraparound.
Abhay Kumara2ae5992025-11-10 14:02:24 +0000597 for e.cur >= e.bufferReset-int32(len(e.hist)) {
khenaidood948f772021-08-11 17:49:24 -0400598 if len(e.hist) == 0 {
599 for i := range e.table[:] {
600 e.table[i] = tableEntry{}
601 }
602 for i := range e.longTable[:] {
603 e.longTable[i] = prevEntry{}
604 }
605 e.cur = e.maxMatchOff
606 e.allDirty = true
607 break
608 }
609 // Shift down everything in the table that isn't already too far away.
610 minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff
611 for i := range e.table[:] {
612 v := e.table[i].offset
613 if v < minOff {
614 v = 0
615 } else {
616 v = v - e.cur + e.maxMatchOff
617 }
618 e.table[i].offset = v
619 }
620 for i := range e.longTable[:] {
621 v := e.longTable[i].offset
622 v2 := e.longTable[i].prev
623 if v < minOff {
624 v = 0
625 v2 = 0
626 } else {
627 v = v - e.cur + e.maxMatchOff
628 if v2 < minOff {
629 v2 = 0
630 } else {
631 v2 = v2 - e.cur + e.maxMatchOff
632 }
633 }
634 e.longTable[i] = prevEntry{
635 offset: v,
636 prev: v2,
637 }
638 }
639 e.allDirty = true
640 e.cur = e.maxMatchOff
641 break
642 }
643
644 s := e.addBlock(src)
645 blk.size = len(src)
646 if len(src) < minNonLiteralBlockSize {
647 blk.extraLits = len(src)
648 blk.literals = blk.literals[:len(src)]
649 copy(blk.literals, src)
650 return
651 }
652
653 // Override src
654 src = e.hist
655 sLimit := int32(len(src)) - inputMargin
656 // stepSize is the number of bytes to skip on every main loop iteration.
657 // It should be >= 1.
658 const stepSize = 1
659
660 const kSearchStrength = 9
661
662 // nextEmit is where in src the next emitLiteral should start from.
663 nextEmit := s
664 cv := load6432(src, s)
665
666 // Relative offsets
667 offset1 := int32(blk.recentOffsets[0])
668 offset2 := int32(blk.recentOffsets[1])
669
670 addLiterals := func(s *seq, until int32) {
671 if until == nextEmit {
672 return
673 }
674 blk.literals = append(blk.literals, src[nextEmit:until]...)
675 s.litLen = uint32(until - nextEmit)
676 }
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530677 if debugEncoder {
khenaidood948f772021-08-11 17:49:24 -0400678 println("recent offsets:", blk.recentOffsets)
679 }
680
681encodeLoop:
682 for {
683 var t int32
684 // We allow the encoder to optionally turn off repeat offsets across blocks
685 canRepeat := len(blk.sequences) > 2
Abhay Kumara2ae5992025-11-10 14:02:24 +0000686 var matched, index0 int32
khenaidood948f772021-08-11 17:49:24 -0400687
688 for {
689 if debugAsserts && canRepeat && offset1 == 0 {
690 panic("offset0 was 0")
691 }
692
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530693 nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
694 nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -0400695 candidateL := e.longTable[nextHashL]
696 candidateS := e.table[nextHashS]
697
698 const repOff = 1
699 repIndex := s - offset1 + repOff
700 off := s + e.cur
701 e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset}
702 e.markLongShardDirty(nextHashL)
703 e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)}
704 e.markShortShardDirty(nextHashS)
Abhay Kumara2ae5992025-11-10 14:02:24 +0000705 index0 = s + 1
khenaidood948f772021-08-11 17:49:24 -0400706
707 if canRepeat {
708 if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) {
709 // Consider history as well.
710 var seq seq
Abhay Kumara2ae5992025-11-10 14:02:24 +0000711 length := 4 + e.matchlen(s+4+repOff, repIndex+4, src)
khenaidood948f772021-08-11 17:49:24 -0400712
Abhay Kumara2ae5992025-11-10 14:02:24 +0000713 seq.matchLen = uint32(length - zstdMinMatch)
khenaidood948f772021-08-11 17:49:24 -0400714
715 // We might be able to match backwards.
716 // Extend as long as we can.
717 start := s + repOff
718 // We end the search early, so we don't risk 0 literals
719 // and have to do special offset treatment.
720 startLimit := nextEmit + 1
721
722 tMin := s - e.maxMatchOff
723 if tMin < 0 {
724 tMin = 0
725 }
726 for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
727 repIndex--
728 start--
729 seq.matchLen++
730 }
731 addLiterals(&seq, start)
732
733 // rep 0
734 seq.offset = 1
735 if debugSequences {
736 println("repeat sequence", seq, "next s:", s)
737 }
738 blk.sequences = append(blk.sequences, seq)
739
740 // Index match start+1 (long) -> s - 1
Abhay Kumara2ae5992025-11-10 14:02:24 +0000741 s += length + repOff
khenaidood948f772021-08-11 17:49:24 -0400742
743 nextEmit = s
744 if s >= sLimit {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530745 if debugEncoder {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000746 println("repeat ended", s, length)
khenaidood948f772021-08-11 17:49:24 -0400747
748 }
749 break encodeLoop
750 }
751 // Index skipped...
752 for index0 < s-1 {
753 cv0 := load6432(src, index0)
754 cv1 := cv0 >> 8
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530755 h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400756 off := index0 + e.cur
757 e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
758 e.markLongShardDirty(h0)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530759 h1 := hashLen(cv1, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -0400760 e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)}
761 e.markShortShardDirty(h1)
762 index0 += 2
763 }
764 cv = load6432(src, s)
765 continue
766 }
767 const repOff2 = 1
768
769 // We deviate from the reference encoder and also check offset 2.
770 // Still slower and not much better, so disabled.
771 // repIndex = s - offset2 + repOff2
772 if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) {
773 // Consider history as well.
774 var seq seq
Abhay Kumara2ae5992025-11-10 14:02:24 +0000775 length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src)
khenaidood948f772021-08-11 17:49:24 -0400776
Abhay Kumara2ae5992025-11-10 14:02:24 +0000777 seq.matchLen = uint32(length - zstdMinMatch)
khenaidood948f772021-08-11 17:49:24 -0400778
779 // We might be able to match backwards.
780 // Extend as long as we can.
781 start := s + repOff2
782 // We end the search early, so we don't risk 0 literals
783 // and have to do special offset treatment.
784 startLimit := nextEmit + 1
785
786 tMin := s - e.maxMatchOff
787 if tMin < 0 {
788 tMin = 0
789 }
790 for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 {
791 repIndex--
792 start--
793 seq.matchLen++
794 }
795 addLiterals(&seq, start)
796
797 // rep 2
798 seq.offset = 2
799 if debugSequences {
800 println("repeat sequence 2", seq, "next s:", s)
801 }
802 blk.sequences = append(blk.sequences, seq)
803
Abhay Kumara2ae5992025-11-10 14:02:24 +0000804 s += length + repOff2
khenaidood948f772021-08-11 17:49:24 -0400805 nextEmit = s
806 if s >= sLimit {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530807 if debugEncoder {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000808 println("repeat ended", s, length)
khenaidood948f772021-08-11 17:49:24 -0400809
810 }
811 break encodeLoop
812 }
813
814 // Index skipped...
815 for index0 < s-1 {
816 cv0 := load6432(src, index0)
817 cv1 := cv0 >> 8
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530818 h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400819 off := index0 + e.cur
820 e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
821 e.markLongShardDirty(h0)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530822 h1 := hashLen(cv1, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -0400823 e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)}
824 e.markShortShardDirty(h1)
825 index0 += 2
826 }
827 cv = load6432(src, s)
828 // Swap offsets
829 offset1, offset2 = offset2, offset1
830 continue
831 }
832 }
833 // Find the offsets of our two matches.
834 coffsetL := candidateL.offset - e.cur
835 coffsetLP := candidateL.prev - e.cur
836
837 // Check if we have a long match.
838 if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
839 // Found a long match, at least 8 bytes.
840 matched = e.matchlen(s+8, coffsetL+8, src) + 8
841 t = coffsetL
842 if debugAsserts && s <= t {
843 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
844 }
845 if debugAsserts && s-t > e.maxMatchOff {
846 panic("s - t >e.maxMatchOff")
847 }
848 if debugMatches {
849 println("long match")
850 }
851
852 if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
853 // Found a long match, at least 8 bytes.
854 prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8
855 if prevMatch > matched {
856 matched = prevMatch
857 t = coffsetLP
858 }
859 if debugAsserts && s <= t {
860 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
861 }
862 if debugAsserts && s-t > e.maxMatchOff {
863 panic("s - t >e.maxMatchOff")
864 }
865 if debugMatches {
866 println("long match")
867 }
868 }
869 break
870 }
871
872 // Check if we have a long match on prev.
873 if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) {
874 // Found a long match, at least 8 bytes.
875 matched = e.matchlen(s+8, coffsetLP+8, src) + 8
876 t = coffsetLP
877 if debugAsserts && s <= t {
878 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
879 }
880 if debugAsserts && s-t > e.maxMatchOff {
881 panic("s - t >e.maxMatchOff")
882 }
883 if debugMatches {
884 println("long match")
885 }
886 break
887 }
888
889 coffsetS := candidateS.offset - e.cur
890
891 // Check if we have a short match.
892 if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val {
893 // found a regular match
894 matched = e.matchlen(s+4, coffsetS+4, src) + 4
895
896 // See if we can find a long match at s+1
897 const checkAt = 1
898 cv := load6432(src, s+checkAt)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530899 nextHashL = hashLen(cv, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400900 candidateL = e.longTable[nextHashL]
901 coffsetL = candidateL.offset - e.cur
902
903 // We can store it, since we have at least a 4 byte match.
904 e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset}
905 e.markLongShardDirty(nextHashL)
906 if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
907 // Found a long match, at least 8 bytes.
908 matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
909 if matchedNext > matched {
910 t = coffsetL
911 s += checkAt
912 matched = matchedNext
913 if debugMatches {
914 println("long match (after short)")
915 }
916 break
917 }
918 }
919
920 // Check prev long...
921 coffsetL = candidateL.prev - e.cur
922 if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) {
923 // Found a long match, at least 8 bytes.
924 matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8
925 if matchedNext > matched {
926 t = coffsetL
927 s += checkAt
928 matched = matchedNext
929 if debugMatches {
930 println("prev long match (after short)")
931 }
932 break
933 }
934 }
935 t = coffsetS
936 if debugAsserts && s <= t {
937 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
938 }
939 if debugAsserts && s-t > e.maxMatchOff {
940 panic("s - t >e.maxMatchOff")
941 }
942 if debugAsserts && t < 0 {
943 panic("t<0")
944 }
945 if debugMatches {
946 println("short match")
947 }
948 break
949 }
950
951 // No match found, move forward in input.
952 s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1))
953 if s >= sLimit {
954 break encodeLoop
955 }
956 cv = load6432(src, s)
957 }
958 // Try to find a better match by searching for a long match at the end of the current best match
959 if s+matched < sLimit {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530960 nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -0400961 cv := load3232(src, s)
962 candidateL := e.longTable[nextHashL]
963 coffsetL := candidateL.offset - 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 {
968 t = coffsetL
969 matched = matchedNext
970 if debugMatches {
971 println("long match at end-of-match")
972 }
973 }
974 }
975
976 // Check prev long...
977 if true {
978 coffsetL = candidateL.prev - e.cur - matched
979 if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
980 // Found a long match, at least 4 bytes.
981 matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4
982 if matchedNext > matched {
983 t = coffsetL
984 matched = matchedNext
985 if debugMatches {
986 println("prev long match at end-of-match")
987 }
988 }
989 }
990 }
991 }
992 // A match has been found. Update recent offsets.
993 offset2 = offset1
994 offset1 = s - t
995
996 if debugAsserts && s <= t {
997 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
998 }
999
1000 if debugAsserts && canRepeat && int(offset1) > len(src) {
1001 panic("invalid offset")
1002 }
1003
1004 // Extend the n-byte match as long as possible.
1005 l := matched
1006
1007 // Extend backwards
1008 tMin := s - e.maxMatchOff
1009 if tMin < 0 {
1010 tMin = 0
1011 }
1012 for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength {
1013 s--
1014 t--
1015 l++
1016 }
1017
1018 // Write our sequence
1019 var seq seq
1020 seq.litLen = uint32(s - nextEmit)
1021 seq.matchLen = uint32(l - zstdMinMatch)
1022 if seq.litLen > 0 {
1023 blk.literals = append(blk.literals, src[nextEmit:s]...)
1024 }
1025 seq.offset = uint32(s-t) + 3
1026 s += l
1027 if debugSequences {
1028 println("sequence", seq, "next s:", s)
1029 }
1030 blk.sequences = append(blk.sequences, seq)
1031 nextEmit = s
1032 if s >= sLimit {
1033 break encodeLoop
1034 }
1035
1036 // Index match start+1 (long) -> s - 1
Abhay Kumara2ae5992025-11-10 14:02:24 +00001037 off := index0 + e.cur
khenaidood948f772021-08-11 17:49:24 -04001038 for index0 < s-1 {
1039 cv0 := load6432(src, index0)
1040 cv1 := cv0 >> 8
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301041 h0 := hashLen(cv0, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -04001042 e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset}
1043 e.markLongShardDirty(h0)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301044 h1 := hashLen(cv1, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -04001045 e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)}
1046 e.markShortShardDirty(h1)
1047 index0 += 2
Abhay Kumara2ae5992025-11-10 14:02:24 +00001048 off += 2
khenaidood948f772021-08-11 17:49:24 -04001049 }
1050
1051 cv = load6432(src, s)
1052 if !canRepeat {
1053 continue
1054 }
1055
1056 // Check offset 2
1057 for {
1058 o2 := s - offset2
1059 if load3232(src, o2) != uint32(cv) {
1060 // Do regular search
1061 break
1062 }
1063
1064 // Store this, since we have it.
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301065 nextHashL := hashLen(cv, betterLongTableBits, betterLongLen)
1066 nextHashS := hashLen(cv, betterShortTableBits, betterShortLen)
khenaidood948f772021-08-11 17:49:24 -04001067
1068 // We have at least 4 byte match.
1069 // No need to check backwards. We come straight from a match
1070 l := 4 + e.matchlen(s+4, o2+4, src)
1071
1072 e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset}
1073 e.markLongShardDirty(nextHashL)
1074 e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)}
1075 e.markShortShardDirty(nextHashS)
1076 seq.matchLen = uint32(l) - zstdMinMatch
1077 seq.litLen = 0
1078
1079 // Since litlen is always 0, this is offset 1.
1080 seq.offset = 1
1081 s += l
1082 nextEmit = s
1083 if debugSequences {
1084 println("sequence", seq, "next s:", s)
1085 }
1086 blk.sequences = append(blk.sequences, seq)
1087
1088 // Swap offset 1 and 2.
1089 offset1, offset2 = offset2, offset1
1090 if s >= sLimit {
1091 // Finished
1092 break encodeLoop
1093 }
1094 cv = load6432(src, s)
1095 }
1096 }
1097
1098 if int(nextEmit) < len(src) {
1099 blk.literals = append(blk.literals, src[nextEmit:]...)
1100 blk.extraLits = len(src) - int(nextEmit)
1101 }
1102 blk.recentOffsets[0] = uint32(offset1)
1103 blk.recentOffsets[1] = uint32(offset2)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301104 if debugEncoder {
khenaidood948f772021-08-11 17:49:24 -04001105 println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits)
1106 }
1107}
1108
1109// ResetDict will reset and set a dictionary if not nil
1110func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) {
1111 e.resetBase(d, singleBlock)
1112 if d != nil {
1113 panic("betterFastEncoder: Reset with dict")
1114 }
1115}
1116
1117// ResetDict will reset and set a dictionary if not nil
1118func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) {
1119 e.resetBase(d, singleBlock)
1120 if d == nil {
1121 return
1122 }
1123 // Init or copy dict table
1124 if len(e.dictTable) != len(e.table) || d.id != e.lastDictID {
1125 if len(e.dictTable) != len(e.table) {
1126 e.dictTable = make([]tableEntry, len(e.table))
1127 }
1128 end := int32(len(d.content)) - 8 + e.maxMatchOff
1129 for i := e.maxMatchOff; i < end; i += 4 {
1130 const hashLog = betterShortTableBits
1131
1132 cv := load6432(d.content, i-e.maxMatchOff)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301133 nextHash := hashLen(cv, hashLog, betterShortLen) // 0 -> 4
1134 nextHash1 := hashLen(cv>>8, hashLog, betterShortLen) // 1 -> 5
1135 nextHash2 := hashLen(cv>>16, hashLog, betterShortLen) // 2 -> 6
1136 nextHash3 := hashLen(cv>>24, hashLog, betterShortLen) // 3 -> 7
khenaidood948f772021-08-11 17:49:24 -04001137 e.dictTable[nextHash] = tableEntry{
1138 val: uint32(cv),
1139 offset: i,
1140 }
1141 e.dictTable[nextHash1] = tableEntry{
1142 val: uint32(cv >> 8),
1143 offset: i + 1,
1144 }
1145 e.dictTable[nextHash2] = tableEntry{
1146 val: uint32(cv >> 16),
1147 offset: i + 2,
1148 }
1149 e.dictTable[nextHash3] = tableEntry{
1150 val: uint32(cv >> 24),
1151 offset: i + 3,
1152 }
1153 }
1154 e.lastDictID = d.id
1155 e.allDirty = true
1156 }
1157
1158 // Init or copy dict table
1159 if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID {
1160 if len(e.dictLongTable) != len(e.longTable) {
1161 e.dictLongTable = make([]prevEntry, len(e.longTable))
1162 }
1163 if len(d.content) >= 8 {
1164 cv := load6432(d.content, 0)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301165 h := hashLen(cv, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -04001166 e.dictLongTable[h] = prevEntry{
1167 offset: e.maxMatchOff,
1168 prev: e.dictLongTable[h].offset,
1169 }
1170
1171 end := int32(len(d.content)) - 8 + e.maxMatchOff
1172 off := 8 // First to read
1173 for i := e.maxMatchOff + 1; i < end; i++ {
1174 cv = cv>>8 | (uint64(d.content[off]) << 56)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301175 h := hashLen(cv, betterLongTableBits, betterLongLen)
khenaidood948f772021-08-11 17:49:24 -04001176 e.dictLongTable[h] = prevEntry{
1177 offset: i,
1178 prev: e.dictLongTable[h].offset,
1179 }
1180 off++
1181 }
1182 }
1183 e.lastDictID = d.id
1184 e.allDirty = true
1185 }
1186
1187 // Reset table to initial state
1188 {
1189 dirtyShardCnt := 0
1190 if !e.allDirty {
1191 for i := range e.shortTableShardDirty {
1192 if e.shortTableShardDirty[i] {
1193 dirtyShardCnt++
1194 }
1195 }
1196 }
1197 const shardCnt = betterShortTableShardCnt
1198 const shardSize = betterShortTableShardSize
1199 if e.allDirty || dirtyShardCnt > shardCnt*4/6 {
1200 copy(e.table[:], e.dictTable)
1201 for i := range e.shortTableShardDirty {
1202 e.shortTableShardDirty[i] = false
1203 }
1204 } else {
1205 for i := range e.shortTableShardDirty {
1206 if !e.shortTableShardDirty[i] {
1207 continue
1208 }
1209
1210 copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize])
1211 e.shortTableShardDirty[i] = false
1212 }
1213 }
1214 }
1215 {
1216 dirtyShardCnt := 0
1217 if !e.allDirty {
1218 for i := range e.shortTableShardDirty {
1219 if e.shortTableShardDirty[i] {
1220 dirtyShardCnt++
1221 }
1222 }
1223 }
1224 const shardCnt = betterLongTableShardCnt
1225 const shardSize = betterLongTableShardSize
1226 if e.allDirty || dirtyShardCnt > shardCnt*4/6 {
1227 copy(e.longTable[:], e.dictLongTable)
1228 for i := range e.longTableShardDirty {
1229 e.longTableShardDirty[i] = false
1230 }
1231 } else {
1232 for i := range e.longTableShardDirty {
1233 if !e.longTableShardDirty[i] {
1234 continue
1235 }
1236
1237 copy(e.longTable[i*shardSize:(i+1)*shardSize], e.dictLongTable[i*shardSize:(i+1)*shardSize])
1238 e.longTableShardDirty[i] = false
1239 }
1240 }
1241 }
1242 e.cur = e.maxMatchOff
1243 e.allDirty = false
1244}
1245
1246func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) {
1247 e.longTableShardDirty[entryNum/betterLongTableShardSize] = true
1248}
1249
1250func (e *betterFastEncoderDict) markShortShardDirty(entryNum uint32) {
1251 e.shortTableShardDirty[entryNum/betterShortTableShardSize] = true
1252}