encode_go.go (18081B)
1 //go:build !amd64 || appengine || !gc || noasm 2 // +build !amd64 appengine !gc noasm 3 4 package s2 5 6 import ( 7 "bytes" 8 "math/bits" 9 ) 10 11 const hasAmd64Asm = false 12 13 // encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It 14 // assumes that the varint-encoded length of the decompressed bytes has already 15 // been written. 16 // 17 // It also assumes that: 18 // 19 // len(dst) >= MaxEncodedLen(len(src)) 20 func encodeBlock(dst, src []byte) (d int) { 21 if len(src) < minNonLiteralBlockSize { 22 return 0 23 } 24 return encodeBlockGo(dst, src) 25 } 26 27 // encodeBlockBetter encodes a non-empty src to a guaranteed-large-enough dst. It 28 // assumes that the varint-encoded length of the decompressed bytes has already 29 // been written. 30 // 31 // It also assumes that: 32 // 33 // len(dst) >= MaxEncodedLen(len(src)) 34 func encodeBlockBetter(dst, src []byte) (d int) { 35 return encodeBlockBetterGo(dst, src) 36 } 37 38 // encodeBlockBetter encodes a non-empty src to a guaranteed-large-enough dst. It 39 // assumes that the varint-encoded length of the decompressed bytes has already 40 // been written. 41 // 42 // It also assumes that: 43 // 44 // len(dst) >= MaxEncodedLen(len(src)) 45 func encodeBlockBetterSnappy(dst, src []byte) (d int) { 46 return encodeBlockBetterSnappyGo(dst, src) 47 } 48 49 // encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It 50 // assumes that the varint-encoded length of the decompressed bytes has already 51 // been written. 52 // 53 // It also assumes that: 54 // 55 // len(dst) >= MaxEncodedLen(len(src)) 56 func encodeBlockSnappy(dst, src []byte) (d int) { 57 if len(src) < minNonLiteralBlockSize { 58 return 0 59 } 60 return encodeBlockSnappyGo(dst, src) 61 } 62 63 // emitLiteral writes a literal chunk and returns the number of bytes written. 64 // 65 // It assumes that: 66 // 67 // dst is long enough to hold the encoded bytes 68 // 0 <= len(lit) && len(lit) <= math.MaxUint32 69 func emitLiteral(dst, lit []byte) int { 70 if len(lit) == 0 { 71 return 0 72 } 73 const num = 63<<2 | tagLiteral 74 i, n := 0, uint(len(lit)-1) 75 switch { 76 case n < 60: 77 dst[0] = uint8(n)<<2 | tagLiteral 78 i = 1 79 case n < 1<<8: 80 dst[1] = uint8(n) 81 dst[0] = 60<<2 | tagLiteral 82 i = 2 83 case n < 1<<16: 84 dst[2] = uint8(n >> 8) 85 dst[1] = uint8(n) 86 dst[0] = 61<<2 | tagLiteral 87 i = 3 88 case n < 1<<24: 89 dst[3] = uint8(n >> 16) 90 dst[2] = uint8(n >> 8) 91 dst[1] = uint8(n) 92 dst[0] = 62<<2 | tagLiteral 93 i = 4 94 default: 95 dst[4] = uint8(n >> 24) 96 dst[3] = uint8(n >> 16) 97 dst[2] = uint8(n >> 8) 98 dst[1] = uint8(n) 99 dst[0] = 63<<2 | tagLiteral 100 i = 5 101 } 102 return i + copy(dst[i:], lit) 103 } 104 105 // emitRepeat writes a repeat chunk and returns the number of bytes written. 106 // Length must be at least 4 and < 1<<24 107 func emitRepeat(dst []byte, offset, length int) int { 108 // Repeat offset, make length cheaper 109 length -= 4 110 if length <= 4 { 111 dst[0] = uint8(length)<<2 | tagCopy1 112 dst[1] = 0 113 return 2 114 } 115 if length < 8 && offset < 2048 { 116 // Encode WITH offset 117 dst[1] = uint8(offset) 118 dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1 119 return 2 120 } 121 if length < (1<<8)+4 { 122 length -= 4 123 dst[2] = uint8(length) 124 dst[1] = 0 125 dst[0] = 5<<2 | tagCopy1 126 return 3 127 } 128 if length < (1<<16)+(1<<8) { 129 length -= 1 << 8 130 dst[3] = uint8(length >> 8) 131 dst[2] = uint8(length >> 0) 132 dst[1] = 0 133 dst[0] = 6<<2 | tagCopy1 134 return 4 135 } 136 const maxRepeat = (1 << 24) - 1 137 length -= 1 << 16 138 left := 0 139 if length > maxRepeat { 140 left = length - maxRepeat + 4 141 length = maxRepeat - 4 142 } 143 dst[4] = uint8(length >> 16) 144 dst[3] = uint8(length >> 8) 145 dst[2] = uint8(length >> 0) 146 dst[1] = 0 147 dst[0] = 7<<2 | tagCopy1 148 if left > 0 { 149 return 5 + emitRepeat(dst[5:], offset, left) 150 } 151 return 5 152 } 153 154 // emitCopy writes a copy chunk and returns the number of bytes written. 155 // 156 // It assumes that: 157 // 158 // dst is long enough to hold the encoded bytes 159 // 1 <= offset && offset <= math.MaxUint32 160 // 4 <= length && length <= 1 << 24 161 func emitCopy(dst []byte, offset, length int) int { 162 if offset >= 65536 { 163 i := 0 164 if length > 64 { 165 // Emit a length 64 copy, encoded as 5 bytes. 166 dst[4] = uint8(offset >> 24) 167 dst[3] = uint8(offset >> 16) 168 dst[2] = uint8(offset >> 8) 169 dst[1] = uint8(offset) 170 dst[0] = 63<<2 | tagCopy4 171 length -= 64 172 if length >= 4 { 173 // Emit remaining as repeats 174 return 5 + emitRepeat(dst[5:], offset, length) 175 } 176 i = 5 177 } 178 if length == 0 { 179 return i 180 } 181 // Emit a copy, offset encoded as 4 bytes. 182 dst[i+0] = uint8(length-1)<<2 | tagCopy4 183 dst[i+1] = uint8(offset) 184 dst[i+2] = uint8(offset >> 8) 185 dst[i+3] = uint8(offset >> 16) 186 dst[i+4] = uint8(offset >> 24) 187 return i + 5 188 } 189 190 // Offset no more than 2 bytes. 191 if length > 64 { 192 off := 3 193 if offset < 2048 { 194 // emit 8 bytes as tagCopy1, rest as repeats. 195 dst[1] = uint8(offset) 196 dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1 197 length -= 8 198 off = 2 199 } else { 200 // Emit a length 60 copy, encoded as 3 bytes. 201 // Emit remaining as repeat value (minimum 4 bytes). 202 dst[2] = uint8(offset >> 8) 203 dst[1] = uint8(offset) 204 dst[0] = 59<<2 | tagCopy2 205 length -= 60 206 } 207 // Emit remaining as repeats, at least 4 bytes remain. 208 return off + emitRepeat(dst[off:], offset, length) 209 } 210 if length >= 12 || offset >= 2048 { 211 // Emit the remaining copy, encoded as 3 bytes. 212 dst[2] = uint8(offset >> 8) 213 dst[1] = uint8(offset) 214 dst[0] = uint8(length-1)<<2 | tagCopy2 215 return 3 216 } 217 // Emit the remaining copy, encoded as 2 bytes. 218 dst[1] = uint8(offset) 219 dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 220 return 2 221 } 222 223 // emitCopyNoRepeat writes a copy chunk and returns the number of bytes written. 224 // 225 // It assumes that: 226 // 227 // dst is long enough to hold the encoded bytes 228 // 1 <= offset && offset <= math.MaxUint32 229 // 4 <= length && length <= 1 << 24 230 func emitCopyNoRepeat(dst []byte, offset, length int) int { 231 if offset >= 65536 { 232 i := 0 233 if length > 64 { 234 // Emit a length 64 copy, encoded as 5 bytes. 235 dst[4] = uint8(offset >> 24) 236 dst[3] = uint8(offset >> 16) 237 dst[2] = uint8(offset >> 8) 238 dst[1] = uint8(offset) 239 dst[0] = 63<<2 | tagCopy4 240 length -= 64 241 if length >= 4 { 242 // Emit remaining as repeats 243 return 5 + emitCopyNoRepeat(dst[5:], offset, length) 244 } 245 i = 5 246 } 247 if length == 0 { 248 return i 249 } 250 // Emit a copy, offset encoded as 4 bytes. 251 dst[i+0] = uint8(length-1)<<2 | tagCopy4 252 dst[i+1] = uint8(offset) 253 dst[i+2] = uint8(offset >> 8) 254 dst[i+3] = uint8(offset >> 16) 255 dst[i+4] = uint8(offset >> 24) 256 return i + 5 257 } 258 259 // Offset no more than 2 bytes. 260 if length > 64 { 261 // Emit a length 60 copy, encoded as 3 bytes. 262 // Emit remaining as repeat value (minimum 4 bytes). 263 dst[2] = uint8(offset >> 8) 264 dst[1] = uint8(offset) 265 dst[0] = 59<<2 | tagCopy2 266 length -= 60 267 // Emit remaining as repeats, at least 4 bytes remain. 268 return 3 + emitCopyNoRepeat(dst[3:], offset, length) 269 } 270 if length >= 12 || offset >= 2048 { 271 // Emit the remaining copy, encoded as 3 bytes. 272 dst[2] = uint8(offset >> 8) 273 dst[1] = uint8(offset) 274 dst[0] = uint8(length-1)<<2 | tagCopy2 275 return 3 276 } 277 // Emit the remaining copy, encoded as 2 bytes. 278 dst[1] = uint8(offset) 279 dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 280 return 2 281 } 282 283 // matchLen returns how many bytes match in a and b 284 // 285 // It assumes that: 286 // 287 // len(a) <= len(b) 288 func matchLen(a []byte, b []byte) int { 289 b = b[:len(a)] 290 var checked int 291 if len(a) > 4 { 292 // Try 4 bytes first 293 if diff := load32(a, 0) ^ load32(b, 0); diff != 0 { 294 return bits.TrailingZeros32(diff) >> 3 295 } 296 // Switch to 8 byte matching. 297 checked = 4 298 a = a[4:] 299 b = b[4:] 300 for len(a) >= 8 { 301 b = b[:len(a)] 302 if diff := load64(a, 0) ^ load64(b, 0); diff != 0 { 303 return checked + (bits.TrailingZeros64(diff) >> 3) 304 } 305 checked += 8 306 a = a[8:] 307 b = b[8:] 308 } 309 } 310 b = b[:len(a)] 311 for i := range a { 312 if a[i] != b[i] { 313 return int(i) + checked 314 } 315 } 316 return len(a) + checked 317 } 318 319 func calcBlockSize(src []byte) (d int) { 320 // Initialize the hash table. 321 const ( 322 tableBits = 13 323 maxTableSize = 1 << tableBits 324 ) 325 326 var table [maxTableSize]uint32 327 328 // sLimit is when to stop looking for offset/length copies. The inputMargin 329 // lets us use a fast path for emitLiteral in the main loop, while we are 330 // looking for copies. 331 sLimit := len(src) - inputMargin 332 333 // Bail if we can't compress to at least this. 334 dstLimit := len(src) - len(src)>>5 - 5 335 336 // nextEmit is where in src the next emitLiteral should start from. 337 nextEmit := 0 338 339 // The encoded form must start with a literal, as there are no previous 340 // bytes to copy, so we start looking for hash matches at s == 1. 341 s := 1 342 cv := load64(src, s) 343 344 // We search for a repeat at -1, but don't output repeats when nextEmit == 0 345 repeat := 1 346 347 for { 348 candidate := 0 349 for { 350 // Next src position to check 351 nextS := s + (s-nextEmit)>>6 + 4 352 if nextS > sLimit { 353 goto emitRemainder 354 } 355 hash0 := hash6(cv, tableBits) 356 hash1 := hash6(cv>>8, tableBits) 357 candidate = int(table[hash0]) 358 candidate2 := int(table[hash1]) 359 table[hash0] = uint32(s) 360 table[hash1] = uint32(s + 1) 361 hash2 := hash6(cv>>16, tableBits) 362 363 // Check repeat at offset checkRep. 364 const checkRep = 1 365 if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { 366 base := s + checkRep 367 // Extend back 368 for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { 369 i-- 370 base-- 371 } 372 d += emitLiteralSize(src[nextEmit:base]) 373 374 // Extend forward 375 candidate := s - repeat + 4 + checkRep 376 s += 4 + checkRep 377 for s <= sLimit { 378 if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { 379 s += bits.TrailingZeros64(diff) >> 3 380 break 381 } 382 s += 8 383 candidate += 8 384 } 385 386 d += emitCopyNoRepeatSize(repeat, s-base) 387 nextEmit = s 388 if s >= sLimit { 389 goto emitRemainder 390 } 391 392 cv = load64(src, s) 393 continue 394 } 395 396 if uint32(cv) == load32(src, candidate) { 397 break 398 } 399 candidate = int(table[hash2]) 400 if uint32(cv>>8) == load32(src, candidate2) { 401 table[hash2] = uint32(s + 2) 402 candidate = candidate2 403 s++ 404 break 405 } 406 table[hash2] = uint32(s + 2) 407 if uint32(cv>>16) == load32(src, candidate) { 408 s += 2 409 break 410 } 411 412 cv = load64(src, nextS) 413 s = nextS 414 } 415 416 // Extend backwards 417 for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { 418 candidate-- 419 s-- 420 } 421 422 // Bail if we exceed the maximum size. 423 if d+(s-nextEmit) > dstLimit { 424 return 0 425 } 426 427 // A 4-byte match has been found. We'll later see if more than 4 bytes 428 // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit 429 // them as literal bytes. 430 431 d += emitLiteralSize(src[nextEmit:s]) 432 433 // Call emitCopy, and then see if another emitCopy could be our next 434 // move. Repeat until we find no match for the input immediately after 435 // what was consumed by the last emitCopy call. 436 // 437 // If we exit this loop normally then we need to call emitLiteral next, 438 // though we don't yet know how big the literal will be. We handle that 439 // by proceeding to the next iteration of the main loop. We also can 440 // exit this loop via goto if we get close to exhausting the input. 441 for { 442 // Invariant: we have a 4-byte match at s, and no need to emit any 443 // literal bytes prior to s. 444 base := s 445 repeat = base - candidate 446 447 // Extend the 4-byte match as long as possible. 448 s += 4 449 candidate += 4 450 for s <= len(src)-8 { 451 if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { 452 s += bits.TrailingZeros64(diff) >> 3 453 break 454 } 455 s += 8 456 candidate += 8 457 } 458 459 d += emitCopyNoRepeatSize(repeat, s-base) 460 if false { 461 // Validate match. 462 a := src[base:s] 463 b := src[base-repeat : base-repeat+(s-base)] 464 if !bytes.Equal(a, b) { 465 panic("mismatch") 466 } 467 } 468 469 nextEmit = s 470 if s >= sLimit { 471 goto emitRemainder 472 } 473 474 if d > dstLimit { 475 // Do we have space for more, if not bail. 476 return 0 477 } 478 // Check for an immediate match, otherwise start search at s+1 479 x := load64(src, s-2) 480 m2Hash := hash6(x, tableBits) 481 currHash := hash6(x>>16, tableBits) 482 candidate = int(table[currHash]) 483 table[m2Hash] = uint32(s - 2) 484 table[currHash] = uint32(s) 485 if uint32(x>>16) != load32(src, candidate) { 486 cv = load64(src, s+1) 487 s++ 488 break 489 } 490 } 491 } 492 493 emitRemainder: 494 if nextEmit < len(src) { 495 // Bail if we exceed the maximum size. 496 if d+len(src)-nextEmit > dstLimit { 497 return 0 498 } 499 d += emitLiteralSize(src[nextEmit:]) 500 } 501 return d 502 } 503 504 func calcBlockSizeSmall(src []byte) (d int) { 505 // Initialize the hash table. 506 const ( 507 tableBits = 9 508 maxTableSize = 1 << tableBits 509 ) 510 511 var table [maxTableSize]uint32 512 513 // sLimit is when to stop looking for offset/length copies. The inputMargin 514 // lets us use a fast path for emitLiteral in the main loop, while we are 515 // looking for copies. 516 sLimit := len(src) - inputMargin 517 518 // Bail if we can't compress to at least this. 519 dstLimit := len(src) - len(src)>>5 - 5 520 521 // nextEmit is where in src the next emitLiteral should start from. 522 nextEmit := 0 523 524 // The encoded form must start with a literal, as there are no previous 525 // bytes to copy, so we start looking for hash matches at s == 1. 526 s := 1 527 cv := load64(src, s) 528 529 // We search for a repeat at -1, but don't output repeats when nextEmit == 0 530 repeat := 1 531 532 for { 533 candidate := 0 534 for { 535 // Next src position to check 536 nextS := s + (s-nextEmit)>>6 + 4 537 if nextS > sLimit { 538 goto emitRemainder 539 } 540 hash0 := hash6(cv, tableBits) 541 hash1 := hash6(cv>>8, tableBits) 542 candidate = int(table[hash0]) 543 candidate2 := int(table[hash1]) 544 table[hash0] = uint32(s) 545 table[hash1] = uint32(s + 1) 546 hash2 := hash6(cv>>16, tableBits) 547 548 // Check repeat at offset checkRep. 549 const checkRep = 1 550 if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { 551 base := s + checkRep 552 // Extend back 553 for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { 554 i-- 555 base-- 556 } 557 d += emitLiteralSize(src[nextEmit:base]) 558 559 // Extend forward 560 candidate := s - repeat + 4 + checkRep 561 s += 4 + checkRep 562 for s <= sLimit { 563 if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { 564 s += bits.TrailingZeros64(diff) >> 3 565 break 566 } 567 s += 8 568 candidate += 8 569 } 570 571 d += emitCopyNoRepeatSize(repeat, s-base) 572 nextEmit = s 573 if s >= sLimit { 574 goto emitRemainder 575 } 576 577 cv = load64(src, s) 578 continue 579 } 580 581 if uint32(cv) == load32(src, candidate) { 582 break 583 } 584 candidate = int(table[hash2]) 585 if uint32(cv>>8) == load32(src, candidate2) { 586 table[hash2] = uint32(s + 2) 587 candidate = candidate2 588 s++ 589 break 590 } 591 table[hash2] = uint32(s + 2) 592 if uint32(cv>>16) == load32(src, candidate) { 593 s += 2 594 break 595 } 596 597 cv = load64(src, nextS) 598 s = nextS 599 } 600 601 // Extend backwards 602 for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] { 603 candidate-- 604 s-- 605 } 606 607 // Bail if we exceed the maximum size. 608 if d+(s-nextEmit) > dstLimit { 609 return 0 610 } 611 612 // A 4-byte match has been found. We'll later see if more than 4 bytes 613 // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit 614 // them as literal bytes. 615 616 d += emitLiteralSize(src[nextEmit:s]) 617 618 // Call emitCopy, and then see if another emitCopy could be our next 619 // move. Repeat until we find no match for the input immediately after 620 // what was consumed by the last emitCopy call. 621 // 622 // If we exit this loop normally then we need to call emitLiteral next, 623 // though we don't yet know how big the literal will be. We handle that 624 // by proceeding to the next iteration of the main loop. We also can 625 // exit this loop via goto if we get close to exhausting the input. 626 for { 627 // Invariant: we have a 4-byte match at s, and no need to emit any 628 // literal bytes prior to s. 629 base := s 630 repeat = base - candidate 631 632 // Extend the 4-byte match as long as possible. 633 s += 4 634 candidate += 4 635 for s <= len(src)-8 { 636 if diff := load64(src, s) ^ load64(src, candidate); diff != 0 { 637 s += bits.TrailingZeros64(diff) >> 3 638 break 639 } 640 s += 8 641 candidate += 8 642 } 643 644 d += emitCopyNoRepeatSize(repeat, s-base) 645 if false { 646 // Validate match. 647 a := src[base:s] 648 b := src[base-repeat : base-repeat+(s-base)] 649 if !bytes.Equal(a, b) { 650 panic("mismatch") 651 } 652 } 653 654 nextEmit = s 655 if s >= sLimit { 656 goto emitRemainder 657 } 658 659 if d > dstLimit { 660 // Do we have space for more, if not bail. 661 return 0 662 } 663 // Check for an immediate match, otherwise start search at s+1 664 x := load64(src, s-2) 665 m2Hash := hash6(x, tableBits) 666 currHash := hash6(x>>16, tableBits) 667 candidate = int(table[currHash]) 668 table[m2Hash] = uint32(s - 2) 669 table[currHash] = uint32(s) 670 if uint32(x>>16) != load32(src, candidate) { 671 cv = load64(src, s+1) 672 s++ 673 break 674 } 675 } 676 } 677 678 emitRemainder: 679 if nextEmit < len(src) { 680 // Bail if we exceed the maximum size. 681 if d+len(src)-nextEmit > dstLimit { 682 return 0 683 } 684 d += emitLiteralSize(src[nextEmit:]) 685 } 686 return d 687 } 688 689 // emitLiteral writes a literal chunk and returns the number of bytes written. 690 // 691 // It assumes that: 692 // 693 // dst is long enough to hold the encoded bytes 694 // 0 <= len(lit) && len(lit) <= math.MaxUint32 695 func emitLiteralSize(lit []byte) int { 696 if len(lit) == 0 { 697 return 0 698 } 699 switch { 700 case len(lit) <= 60: 701 return len(lit) + 1 702 case len(lit) <= 1<<8: 703 return len(lit) + 2 704 case len(lit) <= 1<<16: 705 return len(lit) + 3 706 case len(lit) <= 1<<24: 707 return len(lit) + 4 708 default: 709 return len(lit) + 5 710 } 711 } 712 713 func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { 714 panic("cvtLZ4BlockAsm should be unreachable") 715 } 716 717 func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { 718 panic("cvtLZ4BlockSnappyAsm should be unreachable") 719 } 720 721 func cvtLZ4sBlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { 722 panic("cvtLZ4sBlockAsm should be unreachable") 723 } 724 725 func cvtLZ4sBlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { 726 panic("cvtLZ4sBlockSnappyAsm should be unreachable") 727 }