enc_fast.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  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.
  4. package zstd
  5. import (
  6. "fmt"
  7. "math"
  8. "math/bits"
  9. "github.com/klauspost/compress/zstd/internal/xxhash"
  10. )
  11. const (
  12. tableBits = 15 // Bits used in the table
  13. tableSize = 1 << tableBits // Size of the table
  14. tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks.
  15. maxMatchLength = 131074
  16. )
  17. type tableEntry struct {
  18. val uint32
  19. offset int32
  20. }
  21. type fastBase struct {
  22. // cur is the offset at the start of hist
  23. cur int32
  24. // maximum offset. Should be at least 2x block size.
  25. maxMatchOff int32
  26. hist []byte
  27. crc *xxhash.Digest
  28. tmp [8]byte
  29. blk *blockEnc
  30. }
  31. type fastEncoder struct {
  32. fastBase
  33. table [tableSize]tableEntry
  34. }
  35. // CRC returns the underlying CRC writer.
  36. func (e *fastBase) CRC() *xxhash.Digest {
  37. return e.crc
  38. }
  39. // AppendCRC will append the CRC to the destination slice and return it.
  40. func (e *fastBase) AppendCRC(dst []byte) []byte {
  41. crc := e.crc.Sum(e.tmp[:0])
  42. dst = append(dst, crc[7], crc[6], crc[5], crc[4])
  43. return dst
  44. }
  45. // WindowSize returns the window size of the encoder,
  46. // or a window size small enough to contain the input size, if > 0.
  47. func (e *fastBase) WindowSize(size int) int32 {
  48. if size > 0 && size < int(e.maxMatchOff) {
  49. b := int32(1) << uint(bits.Len(uint(size)))
  50. // Keep minimum window.
  51. if b < 1024 {
  52. b = 1024
  53. }
  54. return b
  55. }
  56. return e.maxMatchOff
  57. }
  58. // Block returns the current block.
  59. func (e *fastBase) Block() *blockEnc {
  60. return e.blk
  61. }
  62. // Encode mimmics functionality in zstd_fast.c
  63. func (e *fastEncoder) Encode(blk *blockEnc, src []byte) {
  64. const (
  65. inputMargin = 8
  66. minNonLiteralBlockSize = 1 + 1 + inputMargin
  67. )
  68. // Protect against e.cur wraparound.
  69. for e.cur >= bufferReset {
  70. if len(e.hist) == 0 {
  71. for i := range e.table[:] {
  72. e.table[i] = tableEntry{}
  73. }
  74. e.cur = e.maxMatchOff
  75. break
  76. }
  77. // Shift down everything in the table that isn't already too far away.
  78. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff
  79. for i := range e.table[:] {
  80. v := e.table[i].offset
  81. if v < minOff {
  82. v = 0
  83. } else {
  84. v = v - e.cur + e.maxMatchOff
  85. }
  86. e.table[i].offset = v
  87. }
  88. e.cur = e.maxMatchOff
  89. break
  90. }
  91. s := e.addBlock(src)
  92. blk.size = len(src)
  93. if len(src) < minNonLiteralBlockSize {
  94. blk.extraLits = len(src)
  95. blk.literals = blk.literals[:len(src)]
  96. copy(blk.literals, src)
  97. return
  98. }
  99. // Override src
  100. src = e.hist
  101. sLimit := int32(len(src)) - inputMargin
  102. // stepSize is the number of bytes to skip on every main loop iteration.
  103. // It should be >= 2.
  104. const stepSize = 2
  105. // TEMPLATE
  106. const hashLog = tableBits
  107. // seems global, but would be nice to tweak.
  108. const kSearchStrength = 8
  109. // nextEmit is where in src the next emitLiteral should start from.
  110. nextEmit := s
  111. cv := load6432(src, s)
  112. // Relative offsets
  113. offset1 := int32(blk.recentOffsets[0])
  114. offset2 := int32(blk.recentOffsets[1])
  115. addLiterals := func(s *seq, until int32) {
  116. if until == nextEmit {
  117. return
  118. }
  119. blk.literals = append(blk.literals, src[nextEmit:until]...)
  120. s.litLen = uint32(until - nextEmit)
  121. }
  122. if debug {
  123. println("recent offsets:", blk.recentOffsets)
  124. }
  125. encodeLoop:
  126. for {
  127. // t will contain the match offset when we find one.
  128. // When existing the search loop, we have already checked 4 bytes.
  129. var t int32
  130. // We will not use repeat offsets across blocks.
  131. // By not using them for the first 3 matches
  132. canRepeat := len(blk.sequences) > 2
  133. for {
  134. if debugAsserts && canRepeat && offset1 == 0 {
  135. panic("offset0 was 0")
  136. }
  137. nextHash := hash6(cv, hashLog)
  138. nextHash2 := hash6(cv>>8, hashLog)
  139. candidate := e.table[nextHash]
  140. candidate2 := e.table[nextHash2]
  141. repIndex := s - offset1 + 2
  142. e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)}
  143. e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)}
  144. if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) {
  145. // Consider history as well.
  146. var seq seq
  147. var length int32
  148. // length = 4 + e.matchlen(s+6, repIndex+4, src)
  149. {
  150. a := src[s+6:]
  151. b := src[repIndex+4:]
  152. endI := len(a) & (math.MaxInt32 - 7)
  153. length = int32(endI) + 4
  154. for i := 0; i < endI; i += 8 {
  155. if diff := load64(a, i) ^ load64(b, i); diff != 0 {
  156. length = int32(i+bits.TrailingZeros64(diff)>>3) + 4
  157. break
  158. }
  159. }
  160. }
  161. seq.matchLen = uint32(length - zstdMinMatch)
  162. // We might be able to match backwards.
  163. // Extend as long as we can.
  164. start := s + 2
  165. // We end the search early, so we don't risk 0 literals
  166. // and have to do special offset treatment.
  167. startLimit := nextEmit + 1
  168. sMin := s - e.maxMatchOff
  169. if sMin < 0 {
  170. sMin = 0
  171. }
  172. for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch {
  173. repIndex--
  174. start--
  175. seq.matchLen++
  176. }
  177. addLiterals(&seq, start)
  178. // rep 0
  179. seq.offset = 1
  180. if debugSequences {
  181. println("repeat sequence", seq, "next s:", s)
  182. }
  183. blk.sequences = append(blk.sequences, seq)
  184. s += length + 2
  185. nextEmit = s
  186. if s >= sLimit {
  187. if debug {
  188. println("repeat ended", s, length)
  189. }
  190. break encodeLoop
  191. }
  192. cv = load6432(src, s)
  193. continue
  194. }
  195. coffset0 := s - (candidate.offset - e.cur)
  196. coffset1 := s - (candidate2.offset - e.cur) + 1
  197. if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val {
  198. // found a regular match
  199. t = candidate.offset - e.cur
  200. if debugAsserts && s <= t {
  201. panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
  202. }
  203. if debugAsserts && s-t > e.maxMatchOff {
  204. panic("s - t >e.maxMatchOff")
  205. }
  206. break
  207. }
  208. if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val {
  209. // found a regular match
  210. t = candidate2.offset - e.cur
  211. s++
  212. if debugAsserts && s <= t {
  213. panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
  214. }
  215. if debugAsserts && s-t > e.maxMatchOff {
  216. panic("s - t >e.maxMatchOff")
  217. }
  218. if debugAsserts && t < 0 {
  219. panic("t<0")
  220. }
  221. break
  222. }
  223. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1))
  224. if s >= sLimit {
  225. break encodeLoop
  226. }
  227. cv = load6432(src, s)
  228. }
  229. // A 4-byte match has been found. We'll later see if more than 4 bytes.
  230. offset2 = offset1
  231. offset1 = s - t
  232. if debugAsserts && s <= t {
  233. panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
  234. }
  235. if debugAsserts && canRepeat && int(offset1) > len(src) {
  236. panic("invalid offset")
  237. }
  238. // Extend the 4-byte match as long as possible.
  239. //l := e.matchlen(s+4, t+4, src) + 4
  240. var l int32
  241. {
  242. a := src[s+4:]
  243. b := src[t+4:]
  244. endI := len(a) & (math.MaxInt32 - 7)
  245. l = int32(endI) + 4
  246. for i := 0; i < endI; i += 8 {
  247. if diff := load64(a, i) ^ load64(b, i); diff != 0 {
  248. l = int32(i+bits.TrailingZeros64(diff)>>3) + 4
  249. break
  250. }
  251. }
  252. }
  253. // Extend backwards
  254. tMin := s - e.maxMatchOff
  255. if tMin < 0 {
  256. tMin = 0
  257. }
  258. for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength {
  259. s--
  260. t--
  261. l++
  262. }
  263. // Write our sequence.
  264. var seq seq
  265. seq.litLen = uint32(s - nextEmit)
  266. seq.matchLen = uint32(l - zstdMinMatch)
  267. if seq.litLen > 0 {
  268. blk.literals = append(blk.literals, src[nextEmit:s]...)
  269. }
  270. // Don't use repeat offsets
  271. seq.offset = uint32(s-t) + 3
  272. s += l
  273. if debugSequences {
  274. println("sequence", seq, "next s:", s)
  275. }
  276. blk.sequences = append(blk.sequences, seq)
  277. nextEmit = s
  278. if s >= sLimit {
  279. break encodeLoop
  280. }
  281. cv = load6432(src, s)
  282. // Check offset 2
  283. if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) {
  284. // We have at least 4 byte match.
  285. // No need to check backwards. We come straight from a match
  286. //l := 4 + e.matchlen(s+4, o2+4, src)
  287. var l int32
  288. {
  289. a := src[s+4:]
  290. b := src[o2+4:]
  291. endI := len(a) & (math.MaxInt32 - 7)
  292. l = int32(endI) + 4
  293. for i := 0; i < endI; i += 8 {
  294. if diff := load64(a, i) ^ load64(b, i); diff != 0 {
  295. l = int32(i+bits.TrailingZeros64(diff)>>3) + 4
  296. break
  297. }
  298. }
  299. }
  300. // Store this, since we have it.
  301. nextHash := hash6(cv, hashLog)
  302. e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)}
  303. seq.matchLen = uint32(l) - zstdMinMatch
  304. seq.litLen = 0
  305. // Since litlen is always 0, this is offset 1.
  306. seq.offset = 1
  307. s += l
  308. nextEmit = s
  309. if debugSequences {
  310. println("sequence", seq, "next s:", s)
  311. }
  312. blk.sequences = append(blk.sequences, seq)
  313. // Swap offset 1 and 2.
  314. offset1, offset2 = offset2, offset1
  315. if s >= sLimit {
  316. break encodeLoop
  317. }
  318. // Prepare next loop.
  319. cv = load6432(src, s)
  320. }
  321. }
  322. if int(nextEmit) < len(src) {
  323. blk.literals = append(blk.literals, src[nextEmit:]...)
  324. blk.extraLits = len(src) - int(nextEmit)
  325. }
  326. blk.recentOffsets[0] = uint32(offset1)
  327. blk.recentOffsets[1] = uint32(offset2)
  328. if debug {
  329. println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits)
  330. }
  331. }
  332. // EncodeNoHist will encode a block with no history and no following blocks.
  333. // Most notable difference is that src will not be copied for history and
  334. // we do not need to check for max match length.
  335. func (e *fastEncoder) EncodeNoHist(blk *blockEnc, src []byte) {
  336. const (
  337. inputMargin = 8
  338. minNonLiteralBlockSize = 1 + 1 + inputMargin
  339. )
  340. if debug {
  341. if len(src) > maxBlockSize {
  342. panic("src too big")
  343. }
  344. }
  345. // Protect against e.cur wraparound.
  346. if e.cur >= bufferReset {
  347. for i := range e.table[:] {
  348. e.table[i] = tableEntry{}
  349. }
  350. e.cur = e.maxMatchOff
  351. }
  352. s := int32(0)
  353. blk.size = len(src)
  354. if len(src) < minNonLiteralBlockSize {
  355. blk.extraLits = len(src)
  356. blk.literals = blk.literals[:len(src)]
  357. copy(blk.literals, src)
  358. return
  359. }
  360. sLimit := int32(len(src)) - inputMargin
  361. // stepSize is the number of bytes to skip on every main loop iteration.
  362. // It should be >= 2.
  363. const stepSize = 2
  364. // TEMPLATE
  365. const hashLog = tableBits
  366. // seems global, but would be nice to tweak.
  367. const kSearchStrength = 8
  368. // nextEmit is where in src the next emitLiteral should start from.
  369. nextEmit := s
  370. cv := load6432(src, s)
  371. // Relative offsets
  372. offset1 := int32(blk.recentOffsets[0])
  373. offset2 := int32(blk.recentOffsets[1])
  374. addLiterals := func(s *seq, until int32) {
  375. if until == nextEmit {
  376. return
  377. }
  378. blk.literals = append(blk.literals, src[nextEmit:until]...)
  379. s.litLen = uint32(until - nextEmit)
  380. }
  381. if debug {
  382. println("recent offsets:", blk.recentOffsets)
  383. }
  384. encodeLoop:
  385. for {
  386. // t will contain the match offset when we find one.
  387. // When existing the search loop, we have already checked 4 bytes.
  388. var t int32
  389. // We will not use repeat offsets across blocks.
  390. // By not using them for the first 3 matches
  391. for {
  392. nextHash := hash6(cv, hashLog)
  393. nextHash2 := hash6(cv>>8, hashLog)
  394. candidate := e.table[nextHash]
  395. candidate2 := e.table[nextHash2]
  396. repIndex := s - offset1 + 2
  397. e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)}
  398. e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)}
  399. if len(blk.sequences) > 2 && load3232(src, repIndex) == uint32(cv>>16) {
  400. // Consider history as well.
  401. var seq seq
  402. // length := 4 + e.matchlen(s+6, repIndex+4, src)
  403. // length := 4 + int32(matchLen(src[s+6:], src[repIndex+4:]))
  404. var length int32
  405. {
  406. a := src[s+6:]
  407. b := src[repIndex+4:]
  408. endI := len(a) & (math.MaxInt32 - 7)
  409. length = int32(endI) + 4
  410. for i := 0; i < endI; i += 8 {
  411. if diff := load64(a, i) ^ load64(b, i); diff != 0 {
  412. length = int32(i+bits.TrailingZeros64(diff)>>3) + 4
  413. break
  414. }
  415. }
  416. }
  417. seq.matchLen = uint32(length - zstdMinMatch)
  418. // We might be able to match backwards.
  419. // Extend as long as we can.
  420. start := s + 2
  421. // We end the search early, so we don't risk 0 literals
  422. // and have to do special offset treatment.
  423. startLimit := nextEmit + 1
  424. sMin := s - e.maxMatchOff
  425. if sMin < 0 {
  426. sMin = 0
  427. }
  428. for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] {
  429. repIndex--
  430. start--
  431. seq.matchLen++
  432. }
  433. addLiterals(&seq, start)
  434. // rep 0
  435. seq.offset = 1
  436. if debugSequences {
  437. println("repeat sequence", seq, "next s:", s)
  438. }
  439. blk.sequences = append(blk.sequences, seq)
  440. s += length + 2
  441. nextEmit = s
  442. if s >= sLimit {
  443. if debug {
  444. println("repeat ended", s, length)
  445. }
  446. break encodeLoop
  447. }
  448. cv = load6432(src, s)
  449. continue
  450. }
  451. coffset0 := s - (candidate.offset - e.cur)
  452. coffset1 := s - (candidate2.offset - e.cur) + 1
  453. if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val {
  454. // found a regular match
  455. t = candidate.offset - e.cur
  456. if debugAsserts && s <= t {
  457. panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
  458. }
  459. if debugAsserts && s-t > e.maxMatchOff {
  460. panic("s - t >e.maxMatchOff")
  461. }
  462. break
  463. }
  464. if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val {
  465. // found a regular match
  466. t = candidate2.offset - e.cur
  467. s++
  468. if debugAsserts && s <= t {
  469. panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
  470. }
  471. if debugAsserts && s-t > e.maxMatchOff {
  472. panic("s - t >e.maxMatchOff")
  473. }
  474. if debugAsserts && t < 0 {
  475. panic("t<0")
  476. }
  477. break
  478. }
  479. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1))
  480. if s >= sLimit {
  481. break encodeLoop
  482. }
  483. cv = load6432(src, s)
  484. }
  485. // A 4-byte match has been found. We'll later see if more than 4 bytes.
  486. offset2 = offset1
  487. offset1 = s - t
  488. if debugAsserts && s <= t {
  489. panic(fmt.Sprintf("s (%d) <= t (%d)", s, t))
  490. }
  491. // Extend the 4-byte match as long as possible.
  492. //l := e.matchlenNoHist(s+4, t+4, src) + 4
  493. // l := int32(matchLen(src[s+4:], src[t+4:])) + 4
  494. var l int32
  495. {
  496. a := src[s+4:]
  497. b := src[t+4:]
  498. endI := len(a) & (math.MaxInt32 - 7)
  499. l = int32(endI) + 4
  500. for i := 0; i < endI; i += 8 {
  501. if diff := load64(a, i) ^ load64(b, i); diff != 0 {
  502. l = int32(i+bits.TrailingZeros64(diff)>>3) + 4
  503. break
  504. }
  505. }
  506. }
  507. // Extend backwards
  508. tMin := s - e.maxMatchOff
  509. if tMin < 0 {
  510. tMin = 0
  511. }
  512. for t > tMin && s > nextEmit && src[t-1] == src[s-1] {
  513. s--
  514. t--
  515. l++
  516. }
  517. // Write our sequence.
  518. var seq seq
  519. seq.litLen = uint32(s - nextEmit)
  520. seq.matchLen = uint32(l - zstdMinMatch)
  521. if seq.litLen > 0 {
  522. blk.literals = append(blk.literals, src[nextEmit:s]...)
  523. }
  524. // Don't use repeat offsets
  525. seq.offset = uint32(s-t) + 3
  526. s += l
  527. if debugSequences {
  528. println("sequence", seq, "next s:", s)
  529. }
  530. blk.sequences = append(blk.sequences, seq)
  531. nextEmit = s
  532. if s >= sLimit {
  533. break encodeLoop
  534. }
  535. cv = load6432(src, s)
  536. // Check offset 2
  537. if o2 := s - offset2; len(blk.sequences) > 2 && load3232(src, o2) == uint32(cv) {
  538. // We have at least 4 byte match.
  539. // No need to check backwards. We come straight from a match
  540. //l := 4 + e.matchlenNoHist(s+4, o2+4, src)
  541. // l := 4 + int32(matchLen(src[s+4:], src[o2+4:]))
  542. var l int32
  543. {
  544. a := src[s+4:]
  545. b := src[o2+4:]
  546. endI := len(a) & (math.MaxInt32 - 7)
  547. l = int32(endI) + 4
  548. for i := 0; i < endI; i += 8 {
  549. if diff := load64(a, i) ^ load64(b, i); diff != 0 {
  550. l = int32(i+bits.TrailingZeros64(diff)>>3) + 4
  551. break
  552. }
  553. }
  554. }
  555. // Store this, since we have it.
  556. nextHash := hash6(cv, hashLog)
  557. e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)}
  558. seq.matchLen = uint32(l) - zstdMinMatch
  559. seq.litLen = 0
  560. // Since litlen is always 0, this is offset 1.
  561. seq.offset = 1
  562. s += l
  563. nextEmit = s
  564. if debugSequences {
  565. println("sequence", seq, "next s:", s)
  566. }
  567. blk.sequences = append(blk.sequences, seq)
  568. // Swap offset 1 and 2.
  569. offset1, offset2 = offset2, offset1
  570. if s >= sLimit {
  571. break encodeLoop
  572. }
  573. // Prepare next loop.
  574. cv = load6432(src, s)
  575. }
  576. }
  577. if int(nextEmit) < len(src) {
  578. blk.literals = append(blk.literals, src[nextEmit:]...)
  579. blk.extraLits = len(src) - int(nextEmit)
  580. }
  581. if debug {
  582. println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits)
  583. }
  584. }
  585. func (e *fastBase) addBlock(src []byte) int32 {
  586. if debugAsserts && e.cur > bufferReset {
  587. panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, bufferReset))
  588. }
  589. // check if we have space already
  590. if len(e.hist)+len(src) > cap(e.hist) {
  591. if cap(e.hist) == 0 {
  592. l := e.maxMatchOff * 2
  593. // Make it at least 1MB.
  594. if l < 1<<20 {
  595. l = 1 << 20
  596. }
  597. e.hist = make([]byte, 0, l)
  598. } else {
  599. if cap(e.hist) < int(e.maxMatchOff*2) {
  600. panic("unexpected buffer size")
  601. }
  602. // Move down
  603. offset := int32(len(e.hist)) - e.maxMatchOff
  604. copy(e.hist[0:e.maxMatchOff], e.hist[offset:])
  605. e.cur += offset
  606. e.hist = e.hist[:e.maxMatchOff]
  607. }
  608. }
  609. s := int32(len(e.hist))
  610. e.hist = append(e.hist, src...)
  611. return s
  612. }
  613. // useBlock will replace the block with the provided one,
  614. // but transfer recent offsets from the previous.
  615. func (e *fastBase) UseBlock(enc *blockEnc) {
  616. enc.reset(e.blk)
  617. e.blk = enc
  618. }
  619. func (e *fastBase) matchlenNoHist(s, t int32, src []byte) int32 {
  620. // Extend the match to be as long as possible.
  621. return int32(matchLen(src[s:], src[t:]))
  622. }
  623. func (e *fastBase) matchlen(s, t int32, src []byte) int32 {
  624. if debugAsserts {
  625. if s < 0 {
  626. err := fmt.Sprintf("s (%d) < 0", s)
  627. panic(err)
  628. }
  629. if t < 0 {
  630. err := fmt.Sprintf("s (%d) < 0", s)
  631. panic(err)
  632. }
  633. if s-t > e.maxMatchOff {
  634. err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff)
  635. panic(err)
  636. }
  637. if len(src)-int(s) > maxCompressedBlockSize {
  638. panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize))
  639. }
  640. }
  641. // Extend the match to be as long as possible.
  642. return int32(matchLen(src[s:], src[t:]))
  643. }
  644. // Reset the encoding table.
  645. func (e *fastBase) Reset() {
  646. if e.blk == nil {
  647. e.blk = &blockEnc{}
  648. e.blk.init()
  649. } else {
  650. e.blk.reset(nil)
  651. }
  652. e.blk.initNewEncode()
  653. if e.crc == nil {
  654. e.crc = xxhash.New()
  655. } else {
  656. e.crc.Reset()
  657. }
  658. if cap(e.hist) < int(e.maxMatchOff*2) {
  659. l := e.maxMatchOff * 2
  660. // Make it at least 1MB.
  661. if l < 1<<20 {
  662. l = 1 << 20
  663. }
  664. e.hist = make([]byte, 0, l)
  665. }
  666. // We offset current position so everything will be out of reach.
  667. // If above reset line, history will be purged.
  668. if e.cur < bufferReset {
  669. e.cur += e.maxMatchOff + int32(len(e.hist))
  670. }
  671. e.hist = e.hist[:0]
  672. }