github.com/bir3/gocompiler@v0.9.2202/extra/compress/zstd/enc_fast.go (about) 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 5 package zstd 6 7 import ( 8 "fmt" 9 ) 10 11 const ( 12 tableBits = 15 // Bits used in the table 13 tableSize = 1 << tableBits // Size of the table 14 tableShardCnt = 1 << (tableBits - dictShardBits) // Number of shards in the table 15 tableShardSize = tableSize / tableShardCnt // Size of an individual shard 16 tableFastHashLen = 6 17 tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. 18 maxMatchLength = 131074 19 ) 20 21 type tableEntry struct { 22 val uint32 23 offset int32 24 } 25 26 type fastEncoder struct { 27 fastBase 28 table [tableSize]tableEntry 29 } 30 31 type fastEncoderDict struct { 32 fastEncoder 33 dictTable []tableEntry 34 tableShardDirty [tableShardCnt]bool 35 allDirty bool 36 } 37 38 // Encode mimmics functionality in zstd_fast.c 39 func (e *fastEncoder) Encode(blk *blockEnc, src []byte) { 40 const ( 41 inputMargin = 8 42 minNonLiteralBlockSize = 1 + 1 + inputMargin 43 ) 44 45 // Protect against e.cur wraparound. 46 for e.cur >= e.bufferReset-int32(len(e.hist)) { 47 if len(e.hist) == 0 { 48 for i := range e.table[:] { 49 e.table[i] = tableEntry{} 50 } 51 e.cur = e.maxMatchOff 52 break 53 } 54 // Shift down everything in the table that isn't already too far away. 55 minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff 56 for i := range e.table[:] { 57 v := e.table[i].offset 58 if v < minOff { 59 v = 0 60 } else { 61 v = v - e.cur + e.maxMatchOff 62 } 63 e.table[i].offset = v 64 } 65 e.cur = e.maxMatchOff 66 break 67 } 68 69 s := e.addBlock(src) 70 blk.size = len(src) 71 if len(src) < minNonLiteralBlockSize { 72 blk.extraLits = len(src) 73 blk.literals = blk.literals[:len(src)] 74 copy(blk.literals, src) 75 return 76 } 77 78 // Override src 79 src = e.hist 80 sLimit := int32(len(src)) - inputMargin 81 // stepSize is the number of bytes to skip on every main loop iteration. 82 // It should be >= 2. 83 const stepSize = 2 84 85 // TEMPLATE 86 const hashLog = tableBits 87 // seems global, but would be nice to tweak. 88 const kSearchStrength = 6 89 90 // nextEmit is where in src the next emitLiteral should start from. 91 nextEmit := s 92 cv := load6432(src, s) 93 94 // Relative offsets 95 offset1 := int32(blk.recentOffsets[0]) 96 offset2 := int32(blk.recentOffsets[1]) 97 98 addLiterals := func(s *seq, until int32) { 99 if until == nextEmit { 100 return 101 } 102 blk.literals = append(blk.literals, src[nextEmit:until]...) 103 s.litLen = uint32(until - nextEmit) 104 } 105 if debugEncoder { 106 println("recent offsets:", blk.recentOffsets) 107 } 108 109 encodeLoop: 110 for { 111 // t will contain the match offset when we find one. 112 // When existing the search loop, we have already checked 4 bytes. 113 var t int32 114 115 // We will not use repeat offsets across blocks. 116 // By not using them for the first 3 matches 117 canRepeat := len(blk.sequences) > 2 118 119 for { 120 if debugAsserts && canRepeat && offset1 == 0 { 121 panic("offset0 was 0") 122 } 123 124 nextHash := hashLen(cv, hashLog, tableFastHashLen) 125 nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) 126 candidate := e.table[nextHash] 127 candidate2 := e.table[nextHash2] 128 repIndex := s - offset1 + 2 129 130 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} 131 e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} 132 133 if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { 134 // Consider history as well. 135 var seq seq 136 var length int32 137 length = 4 + e.matchlen(s+6, repIndex+4, src) 138 seq.matchLen = uint32(length - zstdMinMatch) 139 140 // We might be able to match backwards. 141 // Extend as long as we can. 142 start := s + 2 143 // We end the search early, so we don't risk 0 literals 144 // and have to do special offset treatment. 145 startLimit := nextEmit + 1 146 147 sMin := s - e.maxMatchOff 148 if sMin < 0 { 149 sMin = 0 150 } 151 for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch { 152 repIndex-- 153 start-- 154 seq.matchLen++ 155 } 156 addLiterals(&seq, start) 157 158 // rep 0 159 seq.offset = 1 160 if debugSequences { 161 println("repeat sequence", seq, "next s:", s) 162 } 163 blk.sequences = append(blk.sequences, seq) 164 s += length + 2 165 nextEmit = s 166 if s >= sLimit { 167 if debugEncoder { 168 println("repeat ended", s, length) 169 170 } 171 break encodeLoop 172 } 173 cv = load6432(src, s) 174 continue 175 } 176 coffset0 := s - (candidate.offset - e.cur) 177 coffset1 := s - (candidate2.offset - e.cur) + 1 178 if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { 179 // found a regular match 180 t = candidate.offset - e.cur 181 if debugAsserts && s <= t { 182 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 183 } 184 if debugAsserts && s-t > e.maxMatchOff { 185 panic("s - t >e.maxMatchOff") 186 } 187 break 188 } 189 190 if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { 191 // found a regular match 192 t = candidate2.offset - e.cur 193 s++ 194 if debugAsserts && s <= t { 195 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 196 } 197 if debugAsserts && s-t > e.maxMatchOff { 198 panic("s - t >e.maxMatchOff") 199 } 200 if debugAsserts && t < 0 { 201 panic("t<0") 202 } 203 break 204 } 205 s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) 206 if s >= sLimit { 207 break encodeLoop 208 } 209 cv = load6432(src, s) 210 } 211 // A 4-byte match has been found. We'll later see if more than 4 bytes. 212 offset2 = offset1 213 offset1 = s - t 214 215 if debugAsserts && s <= t { 216 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 217 } 218 219 if debugAsserts && canRepeat && int(offset1) > len(src) { 220 panic("invalid offset") 221 } 222 223 // Extend the 4-byte match as long as possible. 224 l := e.matchlen(s+4, t+4, src) + 4 225 226 // Extend backwards 227 tMin := s - e.maxMatchOff 228 if tMin < 0 { 229 tMin = 0 230 } 231 for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { 232 s-- 233 t-- 234 l++ 235 } 236 237 // Write our sequence. 238 var seq seq 239 seq.litLen = uint32(s - nextEmit) 240 seq.matchLen = uint32(l - zstdMinMatch) 241 if seq.litLen > 0 { 242 blk.literals = append(blk.literals, src[nextEmit:s]...) 243 } 244 // Don't use repeat offsets 245 seq.offset = uint32(s-t) + 3 246 s += l 247 if debugSequences { 248 println("sequence", seq, "next s:", s) 249 } 250 blk.sequences = append(blk.sequences, seq) 251 nextEmit = s 252 if s >= sLimit { 253 break encodeLoop 254 } 255 cv = load6432(src, s) 256 257 // Check offset 2 258 if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) { 259 // We have at least 4 byte match. 260 // No need to check backwards. We come straight from a match 261 l := 4 + e.matchlen(s+4, o2+4, src) 262 263 // Store this, since we have it. 264 nextHash := hashLen(cv, hashLog, tableFastHashLen) 265 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} 266 seq.matchLen = uint32(l) - zstdMinMatch 267 seq.litLen = 0 268 // Since litlen is always 0, this is offset 1. 269 seq.offset = 1 270 s += l 271 nextEmit = s 272 if debugSequences { 273 println("sequence", seq, "next s:", s) 274 } 275 blk.sequences = append(blk.sequences, seq) 276 277 // Swap offset 1 and 2. 278 offset1, offset2 = offset2, offset1 279 if s >= sLimit { 280 break encodeLoop 281 } 282 // Prepare next loop. 283 cv = load6432(src, s) 284 } 285 } 286 287 if int(nextEmit) < len(src) { 288 blk.literals = append(blk.literals, src[nextEmit:]...) 289 blk.extraLits = len(src) - int(nextEmit) 290 } 291 blk.recentOffsets[0] = uint32(offset1) 292 blk.recentOffsets[1] = uint32(offset2) 293 if debugEncoder { 294 println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) 295 } 296 } 297 298 // EncodeNoHist will encode a block with no history and no following blocks. 299 // Most notable difference is that src will not be copied for history and 300 // we do not need to check for max match length. 301 func (e *fastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { 302 const ( 303 inputMargin = 8 304 minNonLiteralBlockSize = 1 + 1 + inputMargin 305 ) 306 if debugEncoder { 307 if len(src) > maxCompressedBlockSize { 308 panic("src too big") 309 } 310 } 311 312 // Protect against e.cur wraparound. 313 if e.cur >= e.bufferReset { 314 for i := range e.table[:] { 315 e.table[i] = tableEntry{} 316 } 317 e.cur = e.maxMatchOff 318 } 319 320 s := int32(0) 321 blk.size = len(src) 322 if len(src) < minNonLiteralBlockSize { 323 blk.extraLits = len(src) 324 blk.literals = blk.literals[:len(src)] 325 copy(blk.literals, src) 326 return 327 } 328 329 sLimit := int32(len(src)) - inputMargin 330 // stepSize is the number of bytes to skip on every main loop iteration. 331 // It should be >= 2. 332 const stepSize = 2 333 334 // TEMPLATE 335 const hashLog = tableBits 336 // seems global, but would be nice to tweak. 337 const kSearchStrength = 6 338 339 // nextEmit is where in src the next emitLiteral should start from. 340 nextEmit := s 341 cv := load6432(src, s) 342 343 // Relative offsets 344 offset1 := int32(blk.recentOffsets[0]) 345 offset2 := int32(blk.recentOffsets[1]) 346 347 addLiterals := func(s *seq, until int32) { 348 if until == nextEmit { 349 return 350 } 351 blk.literals = append(blk.literals, src[nextEmit:until]...) 352 s.litLen = uint32(until - nextEmit) 353 } 354 if debugEncoder { 355 println("recent offsets:", blk.recentOffsets) 356 } 357 358 encodeLoop: 359 for { 360 // t will contain the match offset when we find one. 361 // When existing the search loop, we have already checked 4 bytes. 362 var t int32 363 364 // We will not use repeat offsets across blocks. 365 // By not using them for the first 3 matches 366 367 for { 368 nextHash := hashLen(cv, hashLog, tableFastHashLen) 369 nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) 370 candidate := e.table[nextHash] 371 candidate2 := e.table[nextHash2] 372 repIndex := s - offset1 + 2 373 374 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} 375 e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} 376 377 if len(blk.sequences) > 2 && load3232(src, repIndex) == uint32(cv>>16) { 378 // Consider history as well. 379 var seq seq 380 length := 4 + e.matchlen(s+6, repIndex+4, src) 381 382 seq.matchLen = uint32(length - zstdMinMatch) 383 384 // We might be able to match backwards. 385 // Extend as long as we can. 386 start := s + 2 387 // We end the search early, so we don't risk 0 literals 388 // and have to do special offset treatment. 389 startLimit := nextEmit + 1 390 391 sMin := s - e.maxMatchOff 392 if sMin < 0 { 393 sMin = 0 394 } 395 for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] { 396 repIndex-- 397 start-- 398 seq.matchLen++ 399 } 400 addLiterals(&seq, start) 401 402 // rep 0 403 seq.offset = 1 404 if debugSequences { 405 println("repeat sequence", seq, "next s:", s) 406 } 407 blk.sequences = append(blk.sequences, seq) 408 s += length + 2 409 nextEmit = s 410 if s >= sLimit { 411 if debugEncoder { 412 println("repeat ended", s, length) 413 414 } 415 break encodeLoop 416 } 417 cv = load6432(src, s) 418 continue 419 } 420 coffset0 := s - (candidate.offset - e.cur) 421 coffset1 := s - (candidate2.offset - e.cur) + 1 422 if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { 423 // found a regular match 424 t = candidate.offset - e.cur 425 if debugAsserts && s <= t { 426 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 427 } 428 if debugAsserts && s-t > e.maxMatchOff { 429 panic("s - t >e.maxMatchOff") 430 } 431 if debugAsserts && t < 0 { 432 panic(fmt.Sprintf("t (%d) < 0, candidate.offset: %d, e.cur: %d, coffset0: %d, e.maxMatchOff: %d", t, candidate.offset, e.cur, coffset0, e.maxMatchOff)) 433 } 434 break 435 } 436 437 if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { 438 // found a regular match 439 t = candidate2.offset - e.cur 440 s++ 441 if debugAsserts && s <= t { 442 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 443 } 444 if debugAsserts && s-t > e.maxMatchOff { 445 panic("s - t >e.maxMatchOff") 446 } 447 if debugAsserts && t < 0 { 448 panic("t<0") 449 } 450 break 451 } 452 s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) 453 if s >= sLimit { 454 break encodeLoop 455 } 456 cv = load6432(src, s) 457 } 458 // A 4-byte match has been found. We'll later see if more than 4 bytes. 459 offset2 = offset1 460 offset1 = s - t 461 462 if debugAsserts && s <= t { 463 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 464 } 465 466 if debugAsserts && t < 0 { 467 panic(fmt.Sprintf("t (%d) < 0 ", t)) 468 } 469 // Extend the 4-byte match as long as possible. 470 l := e.matchlen(s+4, t+4, src) + 4 471 472 // Extend backwards 473 tMin := s - e.maxMatchOff 474 if tMin < 0 { 475 tMin = 0 476 } 477 for t > tMin && s > nextEmit && src[t-1] == src[s-1] { 478 s-- 479 t-- 480 l++ 481 } 482 483 // Write our sequence. 484 var seq seq 485 seq.litLen = uint32(s - nextEmit) 486 seq.matchLen = uint32(l - zstdMinMatch) 487 if seq.litLen > 0 { 488 blk.literals = append(blk.literals, src[nextEmit:s]...) 489 } 490 // Don't use repeat offsets 491 seq.offset = uint32(s-t) + 3 492 s += l 493 if debugSequences { 494 println("sequence", seq, "next s:", s) 495 } 496 blk.sequences = append(blk.sequences, seq) 497 nextEmit = s 498 if s >= sLimit { 499 break encodeLoop 500 } 501 cv = load6432(src, s) 502 503 // Check offset 2 504 if o2 := s - offset2; len(blk.sequences) > 2 && load3232(src, o2) == uint32(cv) { 505 // We have at least 4 byte match. 506 // No need to check backwards. We come straight from a match 507 l := 4 + e.matchlen(s+4, o2+4, src) 508 509 // Store this, since we have it. 510 nextHash := hashLen(cv, hashLog, tableFastHashLen) 511 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} 512 seq.matchLen = uint32(l) - zstdMinMatch 513 seq.litLen = 0 514 // Since litlen is always 0, this is offset 1. 515 seq.offset = 1 516 s += l 517 nextEmit = s 518 if debugSequences { 519 println("sequence", seq, "next s:", s) 520 } 521 blk.sequences = append(blk.sequences, seq) 522 523 // Swap offset 1 and 2. 524 offset1, offset2 = offset2, offset1 525 if s >= sLimit { 526 break encodeLoop 527 } 528 // Prepare next loop. 529 cv = load6432(src, s) 530 } 531 } 532 533 if int(nextEmit) < len(src) { 534 blk.literals = append(blk.literals, src[nextEmit:]...) 535 blk.extraLits = len(src) - int(nextEmit) 536 } 537 if debugEncoder { 538 println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) 539 } 540 // We do not store history, so we must offset e.cur to avoid false matches for next user. 541 if e.cur < e.bufferReset { 542 e.cur += int32(len(src)) 543 } 544 } 545 546 // Encode will encode the content, with a dictionary if initialized for it. 547 func (e *fastEncoderDict) Encode(blk *blockEnc, src []byte) { 548 const ( 549 inputMargin = 8 550 minNonLiteralBlockSize = 1 + 1 + inputMargin 551 ) 552 if e.allDirty || len(src) > 32<<10 { 553 e.fastEncoder.Encode(blk, src) 554 e.allDirty = true 555 return 556 } 557 // Protect against e.cur wraparound. 558 for e.cur >= e.bufferReset-int32(len(e.hist)) { 559 if len(e.hist) == 0 { 560 e.table = [tableSize]tableEntry{} 561 e.cur = e.maxMatchOff 562 break 563 } 564 // Shift down everything in the table that isn't already too far away. 565 minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff 566 for i := range e.table[:] { 567 v := e.table[i].offset 568 if v < minOff { 569 v = 0 570 } else { 571 v = v - e.cur + e.maxMatchOff 572 } 573 e.table[i].offset = v 574 } 575 e.cur = e.maxMatchOff 576 break 577 } 578 579 s := e.addBlock(src) 580 blk.size = len(src) 581 if len(src) < minNonLiteralBlockSize { 582 blk.extraLits = len(src) 583 blk.literals = blk.literals[:len(src)] 584 copy(blk.literals, src) 585 return 586 } 587 588 // Override src 589 src = e.hist 590 sLimit := int32(len(src)) - inputMargin 591 // stepSize is the number of bytes to skip on every main loop iteration. 592 // It should be >= 2. 593 const stepSize = 2 594 595 // TEMPLATE 596 const hashLog = tableBits 597 // seems global, but would be nice to tweak. 598 const kSearchStrength = 7 599 600 // nextEmit is where in src the next emitLiteral should start from. 601 nextEmit := s 602 cv := load6432(src, s) 603 604 // Relative offsets 605 offset1 := int32(blk.recentOffsets[0]) 606 offset2 := int32(blk.recentOffsets[1]) 607 608 addLiterals := func(s *seq, until int32) { 609 if until == nextEmit { 610 return 611 } 612 blk.literals = append(blk.literals, src[nextEmit:until]...) 613 s.litLen = uint32(until - nextEmit) 614 } 615 if debugEncoder { 616 println("recent offsets:", blk.recentOffsets) 617 } 618 619 encodeLoop: 620 for { 621 // t will contain the match offset when we find one. 622 // When existing the search loop, we have already checked 4 bytes. 623 var t int32 624 625 // We will not use repeat offsets across blocks. 626 // By not using them for the first 3 matches 627 canRepeat := len(blk.sequences) > 2 628 629 for { 630 if debugAsserts && canRepeat && offset1 == 0 { 631 panic("offset0 was 0") 632 } 633 634 nextHash := hashLen(cv, hashLog, tableFastHashLen) 635 nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) 636 candidate := e.table[nextHash] 637 candidate2 := e.table[nextHash2] 638 repIndex := s - offset1 + 2 639 640 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} 641 e.markShardDirty(nextHash) 642 e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} 643 e.markShardDirty(nextHash2) 644 645 if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { 646 // Consider history as well. 647 var seq seq 648 var length int32 649 length = 4 + e.matchlen(s+6, repIndex+4, src) 650 651 seq.matchLen = uint32(length - zstdMinMatch) 652 653 // We might be able to match backwards. 654 // Extend as long as we can. 655 start := s + 2 656 // We end the search early, so we don't risk 0 literals 657 // and have to do special offset treatment. 658 startLimit := nextEmit + 1 659 660 sMin := s - e.maxMatchOff 661 if sMin < 0 { 662 sMin = 0 663 } 664 for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch { 665 repIndex-- 666 start-- 667 seq.matchLen++ 668 } 669 addLiterals(&seq, start) 670 671 // rep 0 672 seq.offset = 1 673 if debugSequences { 674 println("repeat sequence", seq, "next s:", s) 675 } 676 blk.sequences = append(blk.sequences, seq) 677 s += length + 2 678 nextEmit = s 679 if s >= sLimit { 680 if debugEncoder { 681 println("repeat ended", s, length) 682 683 } 684 break encodeLoop 685 } 686 cv = load6432(src, s) 687 continue 688 } 689 coffset0 := s - (candidate.offset - e.cur) 690 coffset1 := s - (candidate2.offset - e.cur) + 1 691 if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { 692 // found a regular match 693 t = candidate.offset - e.cur 694 if debugAsserts && s <= t { 695 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 696 } 697 if debugAsserts && s-t > e.maxMatchOff { 698 panic("s - t >e.maxMatchOff") 699 } 700 break 701 } 702 703 if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { 704 // found a regular match 705 t = candidate2.offset - e.cur 706 s++ 707 if debugAsserts && s <= t { 708 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 709 } 710 if debugAsserts && s-t > e.maxMatchOff { 711 panic("s - t >e.maxMatchOff") 712 } 713 if debugAsserts && t < 0 { 714 panic("t<0") 715 } 716 break 717 } 718 s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) 719 if s >= sLimit { 720 break encodeLoop 721 } 722 cv = load6432(src, s) 723 } 724 // A 4-byte match has been found. We'll later see if more than 4 bytes. 725 offset2 = offset1 726 offset1 = s - t 727 728 if debugAsserts && s <= t { 729 panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) 730 } 731 732 if debugAsserts && canRepeat && int(offset1) > len(src) { 733 panic("invalid offset") 734 } 735 736 // Extend the 4-byte match as long as possible. 737 l := e.matchlen(s+4, t+4, src) + 4 738 739 // Extend backwards 740 tMin := s - e.maxMatchOff 741 if tMin < 0 { 742 tMin = 0 743 } 744 for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { 745 s-- 746 t-- 747 l++ 748 } 749 750 // Write our sequence. 751 var seq seq 752 seq.litLen = uint32(s - nextEmit) 753 seq.matchLen = uint32(l - zstdMinMatch) 754 if seq.litLen > 0 { 755 blk.literals = append(blk.literals, src[nextEmit:s]...) 756 } 757 // Don't use repeat offsets 758 seq.offset = uint32(s-t) + 3 759 s += l 760 if debugSequences { 761 println("sequence", seq, "next s:", s) 762 } 763 blk.sequences = append(blk.sequences, seq) 764 nextEmit = s 765 if s >= sLimit { 766 break encodeLoop 767 } 768 cv = load6432(src, s) 769 770 // Check offset 2 771 if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) { 772 // We have at least 4 byte match. 773 // No need to check backwards. We come straight from a match 774 l := 4 + e.matchlen(s+4, o2+4, src) 775 776 // Store this, since we have it. 777 nextHash := hashLen(cv, hashLog, tableFastHashLen) 778 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} 779 e.markShardDirty(nextHash) 780 seq.matchLen = uint32(l) - zstdMinMatch 781 seq.litLen = 0 782 // Since litlen is always 0, this is offset 1. 783 seq.offset = 1 784 s += l 785 nextEmit = s 786 if debugSequences { 787 println("sequence", seq, "next s:", s) 788 } 789 blk.sequences = append(blk.sequences, seq) 790 791 // Swap offset 1 and 2. 792 offset1, offset2 = offset2, offset1 793 if s >= sLimit { 794 break encodeLoop 795 } 796 // Prepare next loop. 797 cv = load6432(src, s) 798 } 799 } 800 801 if int(nextEmit) < len(src) { 802 blk.literals = append(blk.literals, src[nextEmit:]...) 803 blk.extraLits = len(src) - int(nextEmit) 804 } 805 blk.recentOffsets[0] = uint32(offset1) 806 blk.recentOffsets[1] = uint32(offset2) 807 if debugEncoder { 808 println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) 809 } 810 } 811 812 // ResetDict will reset and set a dictionary if not nil 813 func (e *fastEncoder) Reset(d *dict, singleBlock bool) { 814 e.resetBase(d, singleBlock) 815 if d != nil { 816 panic("fastEncoder: Reset with dict") 817 } 818 } 819 820 // ResetDict will reset and set a dictionary if not nil 821 func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { 822 e.resetBase(d, singleBlock) 823 if d == nil { 824 return 825 } 826 827 // Init or copy dict table 828 if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { 829 if len(e.dictTable) != len(e.table) { 830 e.dictTable = make([]tableEntry, len(e.table)) 831 } 832 if true { 833 end := e.maxMatchOff + int32(len(d.content)) - 8 834 for i := e.maxMatchOff; i < end; i += 3 { 835 const hashLog = tableBits 836 837 cv := load6432(d.content, i-e.maxMatchOff) 838 nextHash := hashLen(cv, hashLog, tableFastHashLen) // 0 -> 5 839 nextHash1 := hashLen(cv>>8, hashLog, tableFastHashLen) // 1 -> 6 840 nextHash2 := hashLen(cv>>16, hashLog, tableFastHashLen) // 2 -> 7 841 e.dictTable[nextHash] = tableEntry{ 842 val: uint32(cv), 843 offset: i, 844 } 845 e.dictTable[nextHash1] = tableEntry{ 846 val: uint32(cv >> 8), 847 offset: i + 1, 848 } 849 e.dictTable[nextHash2] = tableEntry{ 850 val: uint32(cv >> 16), 851 offset: i + 2, 852 } 853 } 854 } 855 e.lastDictID = d.id 856 e.allDirty = true 857 } 858 859 e.cur = e.maxMatchOff 860 dirtyShardCnt := 0 861 if !e.allDirty { 862 for i := range e.tableShardDirty { 863 if e.tableShardDirty[i] { 864 dirtyShardCnt++ 865 } 866 } 867 } 868 869 const shardCnt = tableShardCnt 870 const shardSize = tableShardSize 871 if e.allDirty || dirtyShardCnt > shardCnt*4/6 { 872 //copy(e.table[:], e.dictTable) 873 e.table = *(*[tableSize]tableEntry)(e.dictTable) 874 for i := range e.tableShardDirty { 875 e.tableShardDirty[i] = false 876 } 877 e.allDirty = false 878 return 879 } 880 for i := range e.tableShardDirty { 881 if !e.tableShardDirty[i] { 882 continue 883 } 884 885 //copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize]) 886 *(*[shardSize]tableEntry)(e.table[i*shardSize:]) = *(*[shardSize]tableEntry)(e.dictTable[i*shardSize:]) 887 e.tableShardDirty[i] = false 888 } 889 e.allDirty = false 890 } 891 892 func (e *fastEncoderDict) markAllShardsDirty() { 893 e.allDirty = true 894 } 895 896 func (e *fastEncoderDict) markShardDirty(entryNum uint32) { 897 e.tableShardDirty[entryNum/tableShardSize] = true 898 }