XzEnc.c (33160B)
1 /* XzEnc.c -- Xz Encode 2 2018-04-28 : Igor Pavlov : Public domain */ 3 4 #include "Precomp.h" 5 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "7zCrc.h" 10 #include "Bra.h" 11 #include "CpuArch.h" 12 13 #ifdef USE_SUBBLOCK 14 #include "Bcj3Enc.c" 15 #include "SbFind.c" 16 #include "SbEnc.c" 17 #endif 18 19 #include "XzEnc.h" 20 21 // #define _7ZIP_ST 22 23 #ifndef _7ZIP_ST 24 #include "MtCoder.h" 25 #else 26 #define MTCODER__THREADS_MAX 1 27 #define MTCODER__BLOCKS_MAX 1 28 #endif 29 30 #define XZ_GET_PAD_SIZE(dataSize) ((4 - ((unsigned)(dataSize) & 3)) & 3) 31 32 /* max pack size for LZMA2 block + check-64bytrs: */ 33 #define XZ_GET_MAX_BLOCK_PACK_SIZE(unpackSize) ((unpackSize) + ((unpackSize) >> 10) + 16 + 64) 34 35 #define XZ_GET_ESTIMATED_BLOCK_TOTAL_PACK_SIZE(unpackSize) (XZ_BLOCK_HEADER_SIZE_MAX + XZ_GET_MAX_BLOCK_PACK_SIZE(unpackSize)) 36 37 38 #define XzBlock_ClearFlags(p) (p)->flags = 0; 39 #define XzBlock_SetNumFilters(p, n) (p)->flags |= ((n) - 1); 40 #define XzBlock_SetHasPackSize(p) (p)->flags |= XZ_BF_PACK_SIZE; 41 #define XzBlock_SetHasUnpackSize(p) (p)->flags |= XZ_BF_UNPACK_SIZE; 42 43 44 static SRes WriteBytes(ISeqOutStream *s, const void *buf, size_t size) 45 { 46 return (ISeqOutStream_Write(s, buf, size) == size) ? SZ_OK : SZ_ERROR_WRITE; 47 } 48 49 static SRes WriteBytesUpdateCrc(ISeqOutStream *s, const void *buf, size_t size, UInt32 *crc) 50 { 51 *crc = CrcUpdate(*crc, buf, size); 52 return WriteBytes(s, buf, size); 53 } 54 55 56 static SRes Xz_WriteHeader(CXzStreamFlags f, ISeqOutStream *s) 57 { 58 UInt32 crc; 59 Byte header[XZ_STREAM_HEADER_SIZE]; 60 memcpy(header, XZ_SIG, XZ_SIG_SIZE); 61 header[XZ_SIG_SIZE] = (Byte)(f >> 8); 62 header[XZ_SIG_SIZE + 1] = (Byte)(f & 0xFF); 63 crc = CrcCalc(header + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE); 64 SetUi32(header + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE, crc); 65 return WriteBytes(s, header, XZ_STREAM_HEADER_SIZE); 66 } 67 68 69 static SRes XzBlock_WriteHeader(const CXzBlock *p, ISeqOutStream *s) 70 { 71 Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; 72 73 unsigned pos = 1; 74 unsigned numFilters, i; 75 header[pos++] = p->flags; 76 77 if (XzBlock_HasPackSize(p)) pos += Xz_WriteVarInt(header + pos, p->packSize); 78 if (XzBlock_HasUnpackSize(p)) pos += Xz_WriteVarInt(header + pos, p->unpackSize); 79 numFilters = XzBlock_GetNumFilters(p); 80 81 for (i = 0; i < numFilters; i++) 82 { 83 const CXzFilter *f = &p->filters[i]; 84 pos += Xz_WriteVarInt(header + pos, f->id); 85 pos += Xz_WriteVarInt(header + pos, f->propsSize); 86 memcpy(header + pos, f->props, f->propsSize); 87 pos += f->propsSize; 88 } 89 90 while ((pos & 3) != 0) 91 header[pos++] = 0; 92 93 header[0] = (Byte)(pos >> 2); 94 SetUi32(header + pos, CrcCalc(header, pos)); 95 return WriteBytes(s, header, pos + 4); 96 } 97 98 99 100 101 typedef struct 102 { 103 size_t numBlocks; 104 size_t size; 105 size_t allocated; 106 Byte *blocks; 107 } CXzEncIndex; 108 109 110 static void XzEncIndex_Construct(CXzEncIndex *p) 111 { 112 p->numBlocks = 0; 113 p->size = 0; 114 p->allocated = 0; 115 p->blocks = NULL; 116 } 117 118 static void XzEncIndex_Init(CXzEncIndex *p) 119 { 120 p->numBlocks = 0; 121 p->size = 0; 122 } 123 124 static void XzEncIndex_Free(CXzEncIndex *p, ISzAllocPtr alloc) 125 { 126 if (p->blocks) 127 { 128 ISzAlloc_Free(alloc, p->blocks); 129 p->blocks = NULL; 130 } 131 p->numBlocks = 0; 132 p->size = 0; 133 p->allocated = 0; 134 } 135 136 137 static SRes XzEncIndex_ReAlloc(CXzEncIndex *p, size_t newSize, ISzAllocPtr alloc) 138 { 139 Byte *blocks = (Byte *)ISzAlloc_Alloc(alloc, newSize); 140 if (!blocks) 141 return SZ_ERROR_MEM; 142 if (p->size != 0) 143 memcpy(blocks, p->blocks, p->size); 144 if (p->blocks) 145 ISzAlloc_Free(alloc, p->blocks); 146 p->blocks = blocks; 147 p->allocated = newSize; 148 return SZ_OK; 149 } 150 151 152 static SRes XzEncIndex_PreAlloc(CXzEncIndex *p, UInt64 numBlocks, UInt64 unpackSize, UInt64 totalSize, ISzAllocPtr alloc) 153 { 154 UInt64 pos; 155 { 156 Byte buf[32]; 157 unsigned pos2 = Xz_WriteVarInt(buf, totalSize); 158 pos2 += Xz_WriteVarInt(buf + pos2, unpackSize); 159 pos = numBlocks * pos2; 160 } 161 162 if (pos <= p->allocated - p->size) 163 return SZ_OK; 164 { 165 UInt64 newSize64 = p->size + pos; 166 size_t newSize = (size_t)newSize64; 167 if (newSize != newSize64) 168 return SZ_ERROR_MEM; 169 return XzEncIndex_ReAlloc(p, newSize, alloc); 170 } 171 } 172 173 174 static SRes XzEncIndex_AddIndexRecord(CXzEncIndex *p, UInt64 unpackSize, UInt64 totalSize, ISzAllocPtr alloc) 175 { 176 Byte buf[32]; 177 unsigned pos = Xz_WriteVarInt(buf, totalSize); 178 pos += Xz_WriteVarInt(buf + pos, unpackSize); 179 180 if (pos > p->allocated - p->size) 181 { 182 size_t newSize = p->allocated * 2 + 16 * 2; 183 if (newSize < p->size + pos) 184 return SZ_ERROR_MEM; 185 RINOK(XzEncIndex_ReAlloc(p, newSize, alloc)); 186 } 187 memcpy(p->blocks + p->size, buf, pos); 188 p->size += pos; 189 p->numBlocks++; 190 return SZ_OK; 191 } 192 193 194 static SRes XzEncIndex_WriteFooter(const CXzEncIndex *p, CXzStreamFlags flags, ISeqOutStream *s) 195 { 196 Byte buf[32]; 197 UInt64 globalPos; 198 UInt32 crc = CRC_INIT_VAL; 199 unsigned pos = 1 + Xz_WriteVarInt(buf + 1, p->numBlocks); 200 201 globalPos = pos; 202 buf[0] = 0; 203 RINOK(WriteBytesUpdateCrc(s, buf, pos, &crc)); 204 RINOK(WriteBytesUpdateCrc(s, p->blocks, p->size, &crc)); 205 globalPos += p->size; 206 207 pos = XZ_GET_PAD_SIZE(globalPos); 208 buf[1] = 0; 209 buf[2] = 0; 210 buf[3] = 0; 211 globalPos += pos; 212 213 crc = CrcUpdate(crc, buf + 4 - pos, pos); 214 SetUi32(buf + 4, CRC_GET_DIGEST(crc)); 215 216 SetUi32(buf + 8 + 4, (UInt32)(globalPos >> 2)); 217 buf[8 + 8] = (Byte)(flags >> 8); 218 buf[8 + 9] = (Byte)(flags & 0xFF); 219 SetUi32(buf + 8, CrcCalc(buf + 8 + 4, 6)); 220 buf[8 + 10] = XZ_FOOTER_SIG_0; 221 buf[8 + 11] = XZ_FOOTER_SIG_1; 222 223 return WriteBytes(s, buf + 4 - pos, pos + 4 + 12); 224 } 225 226 227 228 /* ---------- CSeqCheckInStream ---------- */ 229 230 typedef struct 231 { 232 ISeqInStream vt; 233 ISeqInStream *realStream; 234 const Byte *data; 235 UInt64 limit; 236 UInt64 processed; 237 int realStreamFinished; 238 CXzCheck check; 239 } CSeqCheckInStream; 240 241 static void SeqCheckInStream_Init(CSeqCheckInStream *p, unsigned checkMode) 242 { 243 p->limit = (UInt64)(Int64)-1; 244 p->processed = 0; 245 p->realStreamFinished = 0; 246 XzCheck_Init(&p->check, checkMode); 247 } 248 249 static void SeqCheckInStream_GetDigest(CSeqCheckInStream *p, Byte *digest) 250 { 251 XzCheck_Final(&p->check, digest); 252 } 253 254 static SRes SeqCheckInStream_Read(const ISeqInStream *pp, void *data, size_t *size) 255 { 256 CSeqCheckInStream *p = CONTAINER_FROM_VTBL(pp, CSeqCheckInStream, vt); 257 size_t size2 = *size; 258 SRes res = SZ_OK; 259 260 if (p->limit != (UInt64)(Int64)-1) 261 { 262 UInt64 rem = p->limit - p->processed; 263 if (size2 > rem) 264 size2 = (size_t)rem; 265 } 266 if (size2 != 0) 267 { 268 if (p->realStream) 269 { 270 res = ISeqInStream_Read(p->realStream, data, &size2); 271 p->realStreamFinished = (size2 == 0) ? 1 : 0; 272 } 273 else 274 memcpy(data, p->data + (size_t)p->processed, size2); 275 XzCheck_Update(&p->check, data, size2); 276 p->processed += size2; 277 } 278 *size = size2; 279 return res; 280 } 281 282 283 /* ---------- CSeqSizeOutStream ---------- */ 284 285 typedef struct 286 { 287 ISeqOutStream vt; 288 ISeqOutStream *realStream; 289 Byte *outBuf; 290 size_t outBufLimit; 291 UInt64 processed; 292 } CSeqSizeOutStream; 293 294 static size_t SeqSizeOutStream_Write(const ISeqOutStream *pp, const void *data, size_t size) 295 { 296 CSeqSizeOutStream *p = CONTAINER_FROM_VTBL(pp, CSeqSizeOutStream, vt); 297 if (p->realStream) 298 size = ISeqOutStream_Write(p->realStream, data, size); 299 else 300 { 301 if (size > p->outBufLimit - (size_t)p->processed) 302 return 0; 303 memcpy(p->outBuf + (size_t)p->processed, data, size); 304 } 305 p->processed += size; 306 return size; 307 } 308 309 310 /* ---------- CSeqInFilter ---------- */ 311 312 #define FILTER_BUF_SIZE (1 << 20) 313 314 typedef struct 315 { 316 ISeqInStream p; 317 ISeqInStream *realStream; 318 IStateCoder StateCoder; 319 Byte *buf; 320 size_t curPos; 321 size_t endPos; 322 int srcWasFinished; 323 } CSeqInFilter; 324 325 326 SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAllocPtr alloc); 327 328 static SRes SeqInFilter_Init(CSeqInFilter *p, const CXzFilter *props, ISzAllocPtr alloc) 329 { 330 if (!p->buf) 331 { 332 p->buf = (Byte *)ISzAlloc_Alloc(alloc, FILTER_BUF_SIZE); 333 if (!p->buf) 334 return SZ_ERROR_MEM; 335 } 336 p->curPos = p->endPos = 0; 337 p->srcWasFinished = 0; 338 RINOK(BraState_SetFromMethod(&p->StateCoder, props->id, 1, alloc)); 339 RINOK(p->StateCoder.SetProps(p->StateCoder.p, props->props, props->propsSize, alloc)); 340 p->StateCoder.Init(p->StateCoder.p); 341 return SZ_OK; 342 } 343 344 345 static SRes SeqInFilter_Read(const ISeqInStream *pp, void *data, size_t *size) 346 { 347 CSeqInFilter *p = CONTAINER_FROM_VTBL(pp, CSeqInFilter, p); 348 size_t sizeOriginal = *size; 349 if (sizeOriginal == 0) 350 return SZ_OK; 351 *size = 0; 352 353 for (;;) 354 { 355 if (!p->srcWasFinished && p->curPos == p->endPos) 356 { 357 p->curPos = 0; 358 p->endPos = FILTER_BUF_SIZE; 359 RINOK(ISeqInStream_Read(p->realStream, p->buf, &p->endPos)); 360 if (p->endPos == 0) 361 p->srcWasFinished = 1; 362 } 363 { 364 SizeT srcLen = p->endPos - p->curPos; 365 ECoderStatus status; 366 SRes res; 367 *size = sizeOriginal; 368 res = p->StateCoder.Code2(p->StateCoder.p, 369 data, size, 370 p->buf + p->curPos, &srcLen, 371 p->srcWasFinished, CODER_FINISH_ANY, 372 &status); 373 p->curPos += srcLen; 374 if (*size != 0 || srcLen == 0 || res != SZ_OK) 375 return res; 376 } 377 } 378 } 379 380 static void SeqInFilter_Construct(CSeqInFilter *p) 381 { 382 p->buf = NULL; 383 p->StateCoder.p = NULL; 384 p->p.Read = SeqInFilter_Read; 385 } 386 387 static void SeqInFilter_Free(CSeqInFilter *p, ISzAllocPtr alloc) 388 { 389 if (p->StateCoder.p) 390 { 391 p->StateCoder.Free(p->StateCoder.p, alloc); 392 p->StateCoder.p = NULL; 393 } 394 if (p->buf) 395 { 396 ISzAlloc_Free(alloc, p->buf); 397 p->buf = NULL; 398 } 399 } 400 401 402 /* ---------- CSbEncInStream ---------- */ 403 404 #ifdef USE_SUBBLOCK 405 406 typedef struct 407 { 408 ISeqInStream vt; 409 ISeqInStream *inStream; 410 CSbEnc enc; 411 } CSbEncInStream; 412 413 static SRes SbEncInStream_Read(const ISeqInStream *pp, void *data, size_t *size) 414 { 415 CSbEncInStream *p = CONTAINER_FROM_VTBL(pp, CSbEncInStream, vt); 416 size_t sizeOriginal = *size; 417 if (sizeOriginal == 0) 418 return SZ_OK; 419 420 for (;;) 421 { 422 if (p->enc.needRead && !p->enc.readWasFinished) 423 { 424 size_t processed = p->enc.needReadSizeMax; 425 RINOK(p->inStream->Read(p->inStream, p->enc.buf + p->enc.readPos, &processed)); 426 p->enc.readPos += processed; 427 if (processed == 0) 428 { 429 p->enc.readWasFinished = True; 430 p->enc.isFinalFinished = True; 431 } 432 p->enc.needRead = False; 433 } 434 435 *size = sizeOriginal; 436 RINOK(SbEnc_Read(&p->enc, data, size)); 437 if (*size != 0 || !p->enc.needRead) 438 return SZ_OK; 439 } 440 } 441 442 void SbEncInStream_Construct(CSbEncInStream *p, ISzAllocPtr alloc) 443 { 444 SbEnc_Construct(&p->enc, alloc); 445 p->vt.Read = SbEncInStream_Read; 446 } 447 448 SRes SbEncInStream_Init(CSbEncInStream *p) 449 { 450 return SbEnc_Init(&p->enc); 451 } 452 453 void SbEncInStream_Free(CSbEncInStream *p) 454 { 455 SbEnc_Free(&p->enc); 456 } 457 458 #endif 459 460 461 462 /* ---------- CXzProps ---------- */ 463 464 465 void XzFilterProps_Init(CXzFilterProps *p) 466 { 467 p->id = 0; 468 p->delta = 0; 469 p->ip = 0; 470 p->ipDefined = False; 471 } 472 473 void XzProps_Init(CXzProps *p) 474 { 475 p->checkId = XZ_CHECK_CRC32; 476 p->blockSize = XZ_PROPS__BLOCK_SIZE__AUTO; 477 p->numBlockThreads_Reduced = -1; 478 p->numBlockThreads_Max = -1; 479 p->numTotalThreads = -1; 480 p->reduceSize = (UInt64)(Int64)-1; 481 p->forceWriteSizesInHeader = 0; 482 // p->forceWriteSizesInHeader = 1; 483 484 XzFilterProps_Init(&p->filterProps); 485 Lzma2EncProps_Init(&p->lzma2Props); 486 } 487 488 489 static void XzEncProps_Normalize_Fixed(CXzProps *p) 490 { 491 UInt64 fileSize; 492 int t1, t1n, t2, t2r, t3; 493 { 494 CLzma2EncProps tp = p->lzma2Props; 495 if (tp.numTotalThreads <= 0) 496 tp.numTotalThreads = p->numTotalThreads; 497 Lzma2EncProps_Normalize(&tp); 498 t1n = tp.numTotalThreads; 499 } 500 501 t1 = p->lzma2Props.numTotalThreads; 502 t2 = p->numBlockThreads_Max; 503 t3 = p->numTotalThreads; 504 505 if (t2 > MTCODER__THREADS_MAX) 506 t2 = MTCODER__THREADS_MAX; 507 508 if (t3 <= 0) 509 { 510 if (t2 <= 0) 511 t2 = 1; 512 t3 = t1n * t2; 513 } 514 else if (t2 <= 0) 515 { 516 t2 = t3 / t1n; 517 if (t2 == 0) 518 { 519 t1 = 1; 520 t2 = t3; 521 } 522 if (t2 > MTCODER__THREADS_MAX) 523 t2 = MTCODER__THREADS_MAX; 524 } 525 else if (t1 <= 0) 526 { 527 t1 = t3 / t2; 528 if (t1 == 0) 529 t1 = 1; 530 } 531 else 532 t3 = t1n * t2; 533 534 p->lzma2Props.numTotalThreads = t1; 535 536 t2r = t2; 537 538 fileSize = p->reduceSize; 539 540 if ((p->blockSize < fileSize || fileSize == (UInt64)(Int64)-1)) 541 p->lzma2Props.lzmaProps.reduceSize = p->blockSize; 542 543 Lzma2EncProps_Normalize(&p->lzma2Props); 544 545 t1 = p->lzma2Props.numTotalThreads; 546 547 { 548 if (t2 > 1 && fileSize != (UInt64)(Int64)-1) 549 { 550 UInt64 numBlocks = fileSize / p->blockSize; 551 if (numBlocks * p->blockSize != fileSize) 552 numBlocks++; 553 if (numBlocks < (unsigned)t2) 554 { 555 t2r = (unsigned)numBlocks; 556 if (t2r == 0) 557 t2r = 1; 558 t3 = t1 * t2r; 559 } 560 } 561 } 562 563 p->numBlockThreads_Max = t2; 564 p->numBlockThreads_Reduced = t2r; 565 p->numTotalThreads = t3; 566 } 567 568 569 static void XzProps_Normalize(CXzProps *p) 570 { 571 /* we normalize xzProps properties, but we normalize only some of CXzProps::lzma2Props properties. 572 Lzma2Enc_SetProps() will normalize lzma2Props later. */ 573 574 if (p->blockSize == XZ_PROPS__BLOCK_SIZE__SOLID) 575 { 576 p->lzma2Props.lzmaProps.reduceSize = p->reduceSize; 577 p->numBlockThreads_Reduced = 1; 578 p->numBlockThreads_Max = 1; 579 if (p->lzma2Props.numTotalThreads <= 0) 580 p->lzma2Props.numTotalThreads = p->numTotalThreads; 581 return; 582 } 583 else 584 { 585 CLzma2EncProps *lzma2 = &p->lzma2Props; 586 if (p->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO) 587 { 588 // xz-auto 589 p->lzma2Props.lzmaProps.reduceSize = p->reduceSize; 590 591 if (lzma2->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID) 592 { 593 // if (xz-auto && lzma2-solid) - we use solid for both 594 p->blockSize = XZ_PROPS__BLOCK_SIZE__SOLID; 595 p->numBlockThreads_Reduced = 1; 596 p->numBlockThreads_Max = 1; 597 if (p->lzma2Props.numTotalThreads <= 0) 598 p->lzma2Props.numTotalThreads = p->numTotalThreads; 599 } 600 else 601 { 602 // if (xz-auto && (lzma2-auto || lzma2-fixed_) 603 // we calculate block size for lzma2 and use that block size for xz, lzma2 uses single-chunk per block 604 CLzma2EncProps tp = p->lzma2Props; 605 if (tp.numTotalThreads <= 0) 606 tp.numTotalThreads = p->numTotalThreads; 607 608 Lzma2EncProps_Normalize(&tp); 609 610 p->blockSize = tp.blockSize; // fixed or solid 611 p->numBlockThreads_Reduced = tp.numBlockThreads_Reduced; 612 p->numBlockThreads_Max = tp.numBlockThreads_Max; 613 if (lzma2->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO) 614 lzma2->blockSize = tp.blockSize; // fixed or solid, LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID 615 if (lzma2->lzmaProps.reduceSize > tp.blockSize && tp.blockSize != LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID) 616 lzma2->lzmaProps.reduceSize = tp.blockSize; 617 lzma2->numBlockThreads_Reduced = 1; 618 lzma2->numBlockThreads_Max = 1; 619 return; 620 } 621 } 622 else 623 { 624 // xz-fixed 625 // we can use xz::reduceSize or xz::blockSize as base for lzmaProps::reduceSize 626 627 p->lzma2Props.lzmaProps.reduceSize = p->reduceSize; 628 { 629 UInt64 r = p->reduceSize; 630 if (r > p->blockSize || r == (UInt64)(Int64)-1) 631 r = p->blockSize; 632 lzma2->lzmaProps.reduceSize = r; 633 } 634 if (lzma2->blockSize == LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO) 635 lzma2->blockSize = LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID; 636 else if (lzma2->blockSize > p->blockSize && lzma2->blockSize != LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID) 637 lzma2->blockSize = p->blockSize; 638 639 XzEncProps_Normalize_Fixed(p); 640 } 641 } 642 } 643 644 645 /* ---------- CLzma2WithFilters ---------- */ 646 647 typedef struct 648 { 649 CLzma2EncHandle lzma2; 650 CSeqInFilter filter; 651 652 #ifdef USE_SUBBLOCK 653 CSbEncInStream sb; 654 #endif 655 } CLzma2WithFilters; 656 657 658 static void Lzma2WithFilters_Construct(CLzma2WithFilters *p) 659 { 660 p->lzma2 = NULL; 661 SeqInFilter_Construct(&p->filter); 662 663 #ifdef USE_SUBBLOCK 664 SbEncInStream_Construct(&p->sb, alloc); 665 #endif 666 } 667 668 669 static SRes Lzma2WithFilters_Create(CLzma2WithFilters *p, ISzAllocPtr alloc, ISzAllocPtr bigAlloc) 670 { 671 if (!p->lzma2) 672 { 673 p->lzma2 = Lzma2Enc_Create(alloc, bigAlloc); 674 if (!p->lzma2) 675 return SZ_ERROR_MEM; 676 } 677 return SZ_OK; 678 } 679 680 681 static void Lzma2WithFilters_Free(CLzma2WithFilters *p, ISzAllocPtr alloc) 682 { 683 #ifdef USE_SUBBLOCK 684 SbEncInStream_Free(&p->sb); 685 #endif 686 687 SeqInFilter_Free(&p->filter, alloc); 688 if (p->lzma2) 689 { 690 Lzma2Enc_Destroy(p->lzma2); 691 p->lzma2 = NULL; 692 } 693 } 694 695 696 typedef struct 697 { 698 UInt64 unpackSize; 699 UInt64 totalSize; 700 size_t headerSize; 701 } CXzEncBlockInfo; 702 703 704 static SRes Xz_CompressBlock( 705 CLzma2WithFilters *lzmaf, 706 707 ISeqOutStream *outStream, 708 Byte *outBufHeader, 709 Byte *outBufData, size_t outBufDataLimit, 710 711 ISeqInStream *inStream, 712 // UInt64 expectedSize, 713 const Byte *inBuf, // used if (!inStream) 714 size_t inBufSize, // used if (!inStream), it's block size, props->blockSize is ignored 715 716 const CXzProps *props, 717 ICompressProgress *progress, 718 int *inStreamFinished, /* only for inStream version */ 719 CXzEncBlockInfo *blockSizes, 720 ISzAllocPtr alloc, 721 ISzAllocPtr allocBig) 722 { 723 CSeqCheckInStream checkInStream; 724 CSeqSizeOutStream seqSizeOutStream; 725 CXzBlock block; 726 unsigned filterIndex = 0; 727 CXzFilter *filter = NULL; 728 const CXzFilterProps *fp = &props->filterProps; 729 if (fp->id == 0) 730 fp = NULL; 731 732 *inStreamFinished = False; 733 734 RINOK(Lzma2WithFilters_Create(lzmaf, alloc, allocBig)); 735 736 RINOK(Lzma2Enc_SetProps(lzmaf->lzma2, &props->lzma2Props)); 737 738 XzBlock_ClearFlags(&block); 739 XzBlock_SetNumFilters(&block, 1 + (fp ? 1 : 0)); 740 741 if (fp) 742 { 743 filter = &block.filters[filterIndex++]; 744 filter->id = fp->id; 745 filter->propsSize = 0; 746 747 if (fp->id == XZ_ID_Delta) 748 { 749 filter->props[0] = (Byte)(fp->delta - 1); 750 filter->propsSize = 1; 751 } 752 else if (fp->ipDefined) 753 { 754 SetUi32(filter->props, fp->ip); 755 filter->propsSize = 4; 756 } 757 } 758 759 { 760 CXzFilter *f = &block.filters[filterIndex++]; 761 f->id = XZ_ID_LZMA2; 762 f->propsSize = 1; 763 f->props[0] = Lzma2Enc_WriteProperties(lzmaf->lzma2); 764 } 765 766 seqSizeOutStream.vt.Write = SeqSizeOutStream_Write; 767 seqSizeOutStream.realStream = outStream; 768 seqSizeOutStream.outBuf = outBufData; 769 seqSizeOutStream.outBufLimit = outBufDataLimit; 770 seqSizeOutStream.processed = 0; 771 772 /* 773 if (expectedSize != (UInt64)(Int64)-1) 774 { 775 block.unpackSize = expectedSize; 776 if (props->blockSize != (UInt64)(Int64)-1) 777 if (expectedSize > props->blockSize) 778 block.unpackSize = props->blockSize; 779 XzBlock_SetHasUnpackSize(&block); 780 } 781 */ 782 783 if (outStream) 784 { 785 RINOK(XzBlock_WriteHeader(&block, &seqSizeOutStream.vt)); 786 } 787 788 checkInStream.vt.Read = SeqCheckInStream_Read; 789 SeqCheckInStream_Init(&checkInStream, props->checkId); 790 791 checkInStream.realStream = inStream; 792 checkInStream.data = inBuf; 793 checkInStream.limit = props->blockSize; 794 if (!inStream) 795 checkInStream.limit = inBufSize; 796 797 if (fp) 798 { 799 #ifdef USE_SUBBLOCK 800 if (fp->id == XZ_ID_Subblock) 801 { 802 lzmaf->sb.inStream = &checkInStream.vt; 803 RINOK(SbEncInStream_Init(&lzmaf->sb)); 804 } 805 else 806 #endif 807 { 808 lzmaf->filter.realStream = &checkInStream.vt; 809 RINOK(SeqInFilter_Init(&lzmaf->filter, filter, alloc)); 810 } 811 } 812 813 { 814 SRes res; 815 Byte *outBuf = NULL; 816 size_t outSize = 0; 817 Bool useStream = (fp || inStream); 818 // useStream = True; 819 820 if (!useStream) 821 { 822 XzCheck_Update(&checkInStream.check, inBuf, inBufSize); 823 checkInStream.processed = inBufSize; 824 } 825 826 if (!outStream) 827 { 828 outBuf = seqSizeOutStream.outBuf; // + (size_t)seqSizeOutStream.processed; 829 outSize = seqSizeOutStream.outBufLimit; // - (size_t)seqSizeOutStream.processed; 830 } 831 832 res = Lzma2Enc_Encode2(lzmaf->lzma2, 833 outBuf ? NULL : &seqSizeOutStream.vt, 834 outBuf, 835 outBuf ? &outSize : NULL, 836 837 useStream ? 838 (fp ? 839 ( 840 #ifdef USE_SUBBLOCK 841 (fp->id == XZ_ID_Subblock) ? &lzmaf->sb.vt: 842 #endif 843 &lzmaf->filter.p) : 844 &checkInStream.vt) : NULL, 845 846 useStream ? NULL : inBuf, 847 useStream ? 0 : inBufSize, 848 849 progress); 850 851 if (outBuf) 852 seqSizeOutStream.processed += outSize; 853 854 RINOK(res); 855 blockSizes->unpackSize = checkInStream.processed; 856 } 857 { 858 Byte buf[4 + 64]; 859 unsigned padSize = XZ_GET_PAD_SIZE(seqSizeOutStream.processed); 860 UInt64 packSize = seqSizeOutStream.processed; 861 862 buf[0] = 0; 863 buf[1] = 0; 864 buf[2] = 0; 865 buf[3] = 0; 866 867 SeqCheckInStream_GetDigest(&checkInStream, buf + 4); 868 RINOK(WriteBytes(&seqSizeOutStream.vt, buf + (4 - padSize), padSize + XzFlags_GetCheckSize((CXzStreamFlags)props->checkId))); 869 870 blockSizes->totalSize = seqSizeOutStream.processed - padSize; 871 872 if (!outStream) 873 { 874 seqSizeOutStream.outBuf = outBufHeader; 875 seqSizeOutStream.outBufLimit = XZ_BLOCK_HEADER_SIZE_MAX; 876 seqSizeOutStream.processed = 0; 877 878 block.unpackSize = blockSizes->unpackSize; 879 XzBlock_SetHasUnpackSize(&block); 880 881 block.packSize = packSize; 882 XzBlock_SetHasPackSize(&block); 883 884 RINOK(XzBlock_WriteHeader(&block, &seqSizeOutStream.vt)); 885 886 blockSizes->headerSize = (size_t)seqSizeOutStream.processed; 887 blockSizes->totalSize += seqSizeOutStream.processed; 888 } 889 } 890 891 if (inStream) 892 *inStreamFinished = checkInStream.realStreamFinished; 893 else 894 { 895 *inStreamFinished = False; 896 if (checkInStream.processed != inBufSize) 897 return SZ_ERROR_FAIL; 898 } 899 900 return SZ_OK; 901 } 902 903 904 905 typedef struct 906 { 907 ICompressProgress vt; 908 ICompressProgress *progress; 909 UInt64 inOffset; 910 UInt64 outOffset; 911 } CCompressProgress_XzEncOffset; 912 913 914 static SRes CompressProgress_XzEncOffset_Progress(const ICompressProgress *pp, UInt64 inSize, UInt64 outSize) 915 { 916 const CCompressProgress_XzEncOffset *p = CONTAINER_FROM_VTBL(pp, CCompressProgress_XzEncOffset, vt); 917 inSize += p->inOffset; 918 outSize += p->outOffset; 919 return ICompressProgress_Progress(p->progress, inSize, outSize); 920 } 921 922 923 924 925 typedef struct 926 { 927 ISzAllocPtr alloc; 928 ISzAllocPtr allocBig; 929 930 CXzProps xzProps; 931 UInt64 expectedDataSize; 932 933 CXzEncIndex xzIndex; 934 935 CLzma2WithFilters lzmaf_Items[MTCODER__THREADS_MAX]; 936 937 size_t outBufSize; /* size of allocated outBufs[i] */ 938 Byte *outBufs[MTCODER__BLOCKS_MAX]; 939 940 #ifndef _7ZIP_ST 941 unsigned checkType; 942 ISeqOutStream *outStream; 943 Bool mtCoder_WasConstructed; 944 CMtCoder mtCoder; 945 CXzEncBlockInfo EncBlocks[MTCODER__BLOCKS_MAX]; 946 #endif 947 948 } CXzEnc; 949 950 951 static void XzEnc_Construct(CXzEnc *p) 952 { 953 unsigned i; 954 955 XzEncIndex_Construct(&p->xzIndex); 956 957 for (i = 0; i < MTCODER__THREADS_MAX; i++) 958 Lzma2WithFilters_Construct(&p->lzmaf_Items[i]); 959 960 #ifndef _7ZIP_ST 961 p->mtCoder_WasConstructed = False; 962 { 963 for (i = 0; i < MTCODER__BLOCKS_MAX; i++) 964 p->outBufs[i] = NULL; 965 p->outBufSize = 0; 966 } 967 #endif 968 } 969 970 971 static void XzEnc_FreeOutBufs(CXzEnc *p) 972 { 973 unsigned i; 974 for (i = 0; i < MTCODER__BLOCKS_MAX; i++) 975 if (p->outBufs[i]) 976 { 977 ISzAlloc_Free(p->alloc, p->outBufs[i]); 978 p->outBufs[i] = NULL; 979 } 980 p->outBufSize = 0; 981 } 982 983 984 static void XzEnc_Free(CXzEnc *p, ISzAllocPtr alloc) 985 { 986 unsigned i; 987 988 XzEncIndex_Free(&p->xzIndex, alloc); 989 990 for (i = 0; i < MTCODER__THREADS_MAX; i++) 991 Lzma2WithFilters_Free(&p->lzmaf_Items[i], alloc); 992 993 #ifndef _7ZIP_ST 994 if (p->mtCoder_WasConstructed) 995 { 996 MtCoder_Destruct(&p->mtCoder); 997 p->mtCoder_WasConstructed = False; 998 } 999 XzEnc_FreeOutBufs(p); 1000 #endif 1001 } 1002 1003 1004 CXzEncHandle XzEnc_Create(ISzAllocPtr alloc, ISzAllocPtr allocBig) 1005 { 1006 CXzEnc *p = (CXzEnc *)ISzAlloc_Alloc(alloc, sizeof(CXzEnc)); 1007 if (!p) 1008 return NULL; 1009 XzEnc_Construct(p); 1010 XzProps_Init(&p->xzProps); 1011 XzProps_Normalize(&p->xzProps); 1012 p->expectedDataSize = (UInt64)(Int64)-1; 1013 p->alloc = alloc; 1014 p->allocBig = allocBig; 1015 return p; 1016 } 1017 1018 1019 void XzEnc_Destroy(CXzEncHandle pp) 1020 { 1021 CXzEnc *p = (CXzEnc *)pp; 1022 XzEnc_Free(p, p->alloc); 1023 ISzAlloc_Free(p->alloc, p); 1024 } 1025 1026 1027 SRes XzEnc_SetProps(CXzEncHandle pp, const CXzProps *props) 1028 { 1029 CXzEnc *p = (CXzEnc *)pp; 1030 p->xzProps = *props; 1031 XzProps_Normalize(&p->xzProps); 1032 return SZ_OK; 1033 } 1034 1035 1036 void XzEnc_SetDataSize(CXzEncHandle pp, UInt64 expectedDataSiize) 1037 { 1038 CXzEnc *p = (CXzEnc *)pp; 1039 p->expectedDataSize = expectedDataSiize; 1040 } 1041 1042 1043 1044 1045 #ifndef _7ZIP_ST 1046 1047 static SRes XzEnc_MtCallback_Code(void *pp, unsigned coderIndex, unsigned outBufIndex, 1048 const Byte *src, size_t srcSize, int finished) 1049 { 1050 CXzEnc *me = (CXzEnc *)pp; 1051 SRes res; 1052 CMtProgressThunk progressThunk; 1053 1054 Byte *dest = me->outBufs[outBufIndex]; 1055 1056 UNUSED_VAR(finished) 1057 1058 { 1059 CXzEncBlockInfo *bInfo = &me->EncBlocks[outBufIndex]; 1060 bInfo->totalSize = 0; 1061 bInfo->unpackSize = 0; 1062 bInfo->headerSize = 0; 1063 } 1064 1065 if (!dest) 1066 { 1067 dest = (Byte *)ISzAlloc_Alloc(me->alloc, me->outBufSize); 1068 if (!dest) 1069 return SZ_ERROR_MEM; 1070 me->outBufs[outBufIndex] = dest; 1071 } 1072 1073 MtProgressThunk_CreateVTable(&progressThunk); 1074 progressThunk.mtProgress = &me->mtCoder.mtProgress; 1075 MtProgressThunk_Init(&progressThunk); 1076 1077 { 1078 CXzEncBlockInfo blockSizes; 1079 int inStreamFinished; 1080 1081 res = Xz_CompressBlock( 1082 &me->lzmaf_Items[coderIndex], 1083 1084 NULL, 1085 dest, 1086 dest + XZ_BLOCK_HEADER_SIZE_MAX, me->outBufSize - XZ_BLOCK_HEADER_SIZE_MAX, 1087 1088 NULL, 1089 // srcSize, // expectedSize 1090 src, srcSize, 1091 1092 &me->xzProps, 1093 &progressThunk.vt, 1094 &inStreamFinished, 1095 &blockSizes, 1096 me->alloc, 1097 me->allocBig); 1098 1099 if (res == SZ_OK) 1100 me->EncBlocks[outBufIndex] = blockSizes; 1101 1102 return res; 1103 } 1104 } 1105 1106 1107 static SRes XzEnc_MtCallback_Write(void *pp, unsigned outBufIndex) 1108 { 1109 CXzEnc *me = (CXzEnc *)pp; 1110 1111 const CXzEncBlockInfo *bInfo = &me->EncBlocks[outBufIndex]; 1112 const Byte *data = me->outBufs[outBufIndex]; 1113 1114 RINOK(WriteBytes(me->outStream, data, bInfo->headerSize)); 1115 1116 { 1117 UInt64 totalPackFull = bInfo->totalSize + XZ_GET_PAD_SIZE(bInfo->totalSize); 1118 RINOK(WriteBytes(me->outStream, data + XZ_BLOCK_HEADER_SIZE_MAX, (size_t)totalPackFull - bInfo->headerSize)); 1119 } 1120 1121 return XzEncIndex_AddIndexRecord(&me->xzIndex, bInfo->unpackSize, bInfo->totalSize, me->alloc); 1122 } 1123 1124 #endif 1125 1126 1127 1128 SRes XzEnc_Encode(CXzEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress) 1129 { 1130 CXzEnc *p = (CXzEnc *)pp; 1131 1132 const CXzProps *props = &p->xzProps; 1133 1134 XzEncIndex_Init(&p->xzIndex); 1135 { 1136 UInt64 numBlocks = 1; 1137 UInt64 blockSize = props->blockSize; 1138 1139 if (blockSize != XZ_PROPS__BLOCK_SIZE__SOLID 1140 && props->reduceSize != (UInt64)(Int64)-1) 1141 { 1142 numBlocks = props->reduceSize / blockSize; 1143 if (numBlocks * blockSize != props->reduceSize) 1144 numBlocks++; 1145 } 1146 else 1147 blockSize = (UInt64)1 << 62; 1148 1149 RINOK(XzEncIndex_PreAlloc(&p->xzIndex, numBlocks, blockSize, XZ_GET_ESTIMATED_BLOCK_TOTAL_PACK_SIZE(blockSize), p->alloc)); 1150 } 1151 1152 RINOK(Xz_WriteHeader((CXzStreamFlags)props->checkId, outStream)); 1153 1154 1155 #ifndef _7ZIP_ST 1156 if (props->numBlockThreads_Reduced > 1) 1157 { 1158 IMtCoderCallback2 vt; 1159 1160 if (!p->mtCoder_WasConstructed) 1161 { 1162 p->mtCoder_WasConstructed = True; 1163 MtCoder_Construct(&p->mtCoder); 1164 } 1165 1166 vt.Code = XzEnc_MtCallback_Code; 1167 vt.Write = XzEnc_MtCallback_Write; 1168 1169 p->checkType = props->checkId; 1170 p->xzProps = *props; 1171 1172 p->outStream = outStream; 1173 1174 p->mtCoder.allocBig = p->allocBig; 1175 p->mtCoder.progress = progress; 1176 p->mtCoder.inStream = inStream; 1177 p->mtCoder.inData = NULL; 1178 p->mtCoder.inDataSize = 0; 1179 p->mtCoder.mtCallback = &vt; 1180 p->mtCoder.mtCallbackObject = p; 1181 1182 if ( props->blockSize == XZ_PROPS__BLOCK_SIZE__SOLID 1183 || props->blockSize == XZ_PROPS__BLOCK_SIZE__AUTO) 1184 return SZ_ERROR_FAIL; 1185 1186 p->mtCoder.blockSize = (size_t)props->blockSize; 1187 if (p->mtCoder.blockSize != props->blockSize) 1188 return SZ_ERROR_PARAM; /* SZ_ERROR_MEM */ 1189 1190 { 1191 size_t destBlockSize = XZ_BLOCK_HEADER_SIZE_MAX + XZ_GET_MAX_BLOCK_PACK_SIZE(p->mtCoder.blockSize); 1192 if (destBlockSize < p->mtCoder.blockSize) 1193 return SZ_ERROR_PARAM; 1194 if (p->outBufSize != destBlockSize) 1195 XzEnc_FreeOutBufs(p); 1196 p->outBufSize = destBlockSize; 1197 } 1198 1199 p->mtCoder.numThreadsMax = props->numBlockThreads_Max; 1200 p->mtCoder.expectedDataSize = p->expectedDataSize; 1201 1202 RINOK(MtCoder_Code(&p->mtCoder)); 1203 } 1204 else 1205 #endif 1206 { 1207 int writeStartSizes; 1208 CCompressProgress_XzEncOffset progress2; 1209 Byte *bufData = NULL; 1210 size_t bufSize = 0; 1211 1212 progress2.vt.Progress = CompressProgress_XzEncOffset_Progress; 1213 progress2.inOffset = 0; 1214 progress2.outOffset = 0; 1215 progress2.progress = progress; 1216 1217 writeStartSizes = 0; 1218 1219 if (props->blockSize != XZ_PROPS__BLOCK_SIZE__SOLID) 1220 { 1221 writeStartSizes = (props->forceWriteSizesInHeader > 0); 1222 1223 if (writeStartSizes) 1224 { 1225 size_t t2; 1226 size_t t = (size_t)props->blockSize; 1227 if (t != props->blockSize) 1228 return SZ_ERROR_PARAM; 1229 t = XZ_GET_MAX_BLOCK_PACK_SIZE(t); 1230 if (t < props->blockSize) 1231 return SZ_ERROR_PARAM; 1232 t2 = XZ_BLOCK_HEADER_SIZE_MAX + t; 1233 if (!p->outBufs[0] || t2 != p->outBufSize) 1234 { 1235 XzEnc_FreeOutBufs(p); 1236 p->outBufs[0] = (Byte *)ISzAlloc_Alloc(p->alloc, t2); 1237 if (!p->outBufs[0]) 1238 return SZ_ERROR_MEM; 1239 p->outBufSize = t2; 1240 } 1241 bufData = p->outBufs[0] + XZ_BLOCK_HEADER_SIZE_MAX; 1242 bufSize = t; 1243 } 1244 } 1245 1246 for (;;) 1247 { 1248 CXzEncBlockInfo blockSizes; 1249 int inStreamFinished; 1250 1251 /* 1252 UInt64 rem = (UInt64)(Int64)-1; 1253 if (props->reduceSize != (UInt64)(Int64)-1 1254 && props->reduceSize >= progress2.inOffset) 1255 rem = props->reduceSize - progress2.inOffset; 1256 */ 1257 1258 blockSizes.headerSize = 0; // for GCC 1259 1260 RINOK(Xz_CompressBlock( 1261 &p->lzmaf_Items[0], 1262 1263 writeStartSizes ? NULL : outStream, 1264 writeStartSizes ? p->outBufs[0] : NULL, 1265 bufData, bufSize, 1266 1267 inStream, 1268 // rem, 1269 NULL, 0, 1270 1271 props, 1272 progress ? &progress2.vt : NULL, 1273 &inStreamFinished, 1274 &blockSizes, 1275 p->alloc, 1276 p->allocBig)); 1277 1278 { 1279 UInt64 totalPackFull = blockSizes.totalSize + XZ_GET_PAD_SIZE(blockSizes.totalSize); 1280 1281 if (writeStartSizes) 1282 { 1283 RINOK(WriteBytes(outStream, p->outBufs[0], blockSizes.headerSize)); 1284 RINOK(WriteBytes(outStream, bufData, (size_t)totalPackFull - blockSizes.headerSize)); 1285 } 1286 1287 RINOK(XzEncIndex_AddIndexRecord(&p->xzIndex, blockSizes.unpackSize, blockSizes.totalSize, p->alloc)); 1288 1289 progress2.inOffset += blockSizes.unpackSize; 1290 progress2.outOffset += totalPackFull; 1291 } 1292 1293 if (inStreamFinished) 1294 break; 1295 } 1296 } 1297 1298 return XzEncIndex_WriteFooter(&p->xzIndex, (CXzStreamFlags)props->checkId, outStream); 1299 } 1300 1301 1302 #include "Alloc.h" 1303 1304 SRes Xz_Encode(ISeqOutStream *outStream, ISeqInStream *inStream, 1305 const CXzProps *props, ICompressProgress *progress) 1306 { 1307 SRes res; 1308 CXzEncHandle xz = XzEnc_Create(&g_Alloc, &g_BigAlloc); 1309 if (!xz) 1310 return SZ_ERROR_MEM; 1311 res = XzEnc_SetProps(xz, props); 1312 if (res == SZ_OK) 1313 res = XzEnc_Encode(xz, outStream, inStream, progress); 1314 XzEnc_Destroy(xz); 1315 return res; 1316 } 1317 1318 1319 SRes Xz_EncodeEmpty(ISeqOutStream *outStream) 1320 { 1321 SRes res; 1322 CXzEncIndex xzIndex; 1323 XzEncIndex_Construct(&xzIndex); 1324 res = Xz_WriteHeader((CXzStreamFlags)0, outStream); 1325 if (res == SZ_OK) 1326 res = XzEncIndex_WriteFooter(&xzIndex, (CXzStreamFlags)0, outStream); 1327 XzEncIndex_Free(&xzIndex, NULL); // g_Alloc 1328 return res; 1329 }