1// MultiOutStream.cpp 2 3#include "StdAfx.h" 4 5// #define DEBUG_VOLUMES 6 7#ifdef DEBUG_VOLUMES 8#include <stdio.h> 9 #define PRF(x) x; 10#else 11 #define PRF(x) 12#endif 13 14#include "../../Common/ComTry.h" 15 16#include "../../Windows/FileDir.h" 17#include "../../Windows/FileFind.h" 18#include "../../Windows/System.h" 19 20#include "MultiOutStream.h" 21 22using namespace NWindows; 23using namespace NFile; 24using namespace NDir; 25 26static const unsigned k_NumVols_MAX = k_VectorSizeMax - 1; 27 // 2; // for debug 28 29/* 30#define UPDATE_HRES(hres, x) \ 31 { const HRESULT res2 = (x); if (hres == SZ_OK) hres = res2; } 32*/ 33 34HRESULT CMultiOutStream::Destruct() 35{ 36 COM_TRY_BEGIN 37 HRESULT hres = S_OK; 38 HRESULT hres3 = S_OK; 39 40 while (!Streams.IsEmpty()) 41 { 42 try 43 { 44 HRESULT hres2; 45 if (NeedDelete) 46 { 47 /* we could call OptReOpen_and_SetSize() to test that we try to delete correct file, 48 but we cannot guarantee that (RealSize) will be correct after Write() or another failures. 49 And we still want to delete files even for such cases. 50 So we don't check for OptReOpen_and_SetSize() here: */ 51 // if (OptReOpen_and_SetSize(Streams.Size() - 1, 0) == S_OK) 52 hres2 = CloseStream_and_DeleteFile(Streams.Size() - 1); 53 } 54 else 55 { 56 hres2 = CloseStream(Streams.Size() - 1); 57 } 58 if (hres == S_OK) 59 hres = hres2; 60 } 61 catch(...) 62 { 63 hres3 = E_OUTOFMEMORY; 64 } 65 66 { 67 /* Stream was released in CloseStream_*() above already, and it was removed from linked list 68 it's some unexpected case, if Stream is still attached here. 69 So the following code is optional: */ 70 CVolStream &s = Streams.Back(); 71 if (s.Stream) 72 { 73 if (hres3 == S_OK) 74 hres3 = E_FAIL; 75 s.Stream.Detach(); 76 /* it will be not failure, even if we call RemoveFromLinkedList() 77 twice for same CVolStream in this Destruct() function */ 78 RemoveFromLinkedList(Streams.Size() - 1); 79 } 80 } 81 Streams.DeleteBack(); 82 // Delete_LastStream_Records(); 83 } 84 85 if (hres == S_OK) 86 hres = hres3; 87 if (hres == S_OK && NumListItems != 0) 88 hres = E_FAIL; 89 return hres; 90 COM_TRY_END 91} 92 93 94CMultiOutStream::~CMultiOutStream() 95{ 96 // we try to avoid exception in destructors 97 Destruct(); 98} 99 100 101void CMultiOutStream::Init(const CRecordVector<UInt64> &sizes) 102{ 103 Streams.Clear(); 104 InitLinkedList(); 105 Sizes = sizes; 106 NeedDelete = true; 107 MTime_Defined = false; 108 FinalVol_WasReopen = false; 109 NumOpenFiles_AllowedMax = NSystem::Get_File_OPEN_MAX_Reduced_for_3_tasks(); 110 111 _streamIndex = 0; 112 _offsetPos = 0; 113 _absPos = 0; 114 _length = 0; 115 _absLimit = (UInt64)(Int64)-1; 116 117 _restrict_Begin = 0; 118 _restrict_End = (UInt64)(Int64)-1; 119 _restrict_Global = 0; 120 121 UInt64 sum = 0; 122 unsigned i = 0; 123 for (i = 0; i < Sizes.Size(); i++) 124 { 125 if (i >= k_NumVols_MAX) 126 { 127 _absLimit = sum; 128 break; 129 } 130 const UInt64 size = Sizes[i]; 131 const UInt64 next = sum + size; 132 if (next < sum) 133 break; 134 sum = next; 135 } 136 137 // if (Sizes.IsEmpty()) throw "no volume sizes"; 138 const UInt64 size = Sizes.Back(); 139 if (size == 0) 140 throw "zero size last volume"; 141 142 if (i == Sizes.Size()) 143 if ((_absLimit - sum) / size >= (k_NumVols_MAX - i)) 144 _absLimit = sum + (k_NumVols_MAX - i) * size; 145} 146 147 148/* IsRestricted(): 149 we must call only if volume is full (s.RealSize==VolSize) or finished. 150 the function doesn't use VolSize and it uses s.RealSize instead. 151 it returns true : if stream is restricted, and we can't close that stream 152 it returns false : if there is no restriction, and we can close that stream 153 Note: (RealSize == 0) (empty volume) on restriction bounds are supposed as non-restricted 154*/ 155bool CMultiOutStream::IsRestricted(const CVolStream &s) const 156{ 157 if (s.Start < _restrict_Global) 158 return true; 159 if (_restrict_Begin == _restrict_End) 160 return false; 161 if (_restrict_Begin <= s.Start) 162 return _restrict_End > s.Start; 163 return _restrict_Begin < s.Start + s.RealSize; 164} 165 166/* 167// this function check also _length and volSize 168bool CMultiOutStream::IsRestricted_for_Close(unsigned index) const 169{ 170 const CVolStream &s = Streams[index]; 171 if (_length <= s.Start) // we don't close streams after the end, because we still can write them later 172 return true; 173 // (_length > s.Start) 174 const UInt64 volSize = GetVolSize_for_Stream(index); 175 if (volSize == 0) 176 return IsRestricted_Empty(s); 177 if (_length - s.Start < volSize) 178 return true; 179 return IsRestricted(s); 180} 181*/ 182 183FString CMultiOutStream::GetFilePath(unsigned index) 184{ 185 FString name; 186 name.Add_UInt32(index + 1); 187 while (name.Len() < 3) 188 name.InsertAtFront(FTEXT('0')); 189 name.Insert(0, Prefix); 190 return name; 191} 192 193 194// we close stream, but we still keep item in Streams[] vector 195HRESULT CMultiOutStream::CloseStream(unsigned index) 196{ 197 CVolStream &s = Streams[index]; 198 if (s.Stream) 199 { 200 RINOK(s.StreamSpec->Close()) 201 // the following two commands must be called together: 202 s.Stream.Release(); 203 RemoveFromLinkedList(index); 204 } 205 return S_OK; 206} 207 208 209// we close stream and delete file, but we still keep item in Streams[] vector 210HRESULT CMultiOutStream::CloseStream_and_DeleteFile(unsigned index) 211{ 212 PRF(printf("\n====== %u, CloseStream_AndDelete \n", index)); 213 RINOK(CloseStream(index)) 214 FString path = GetFilePath(index); 215 path += Streams[index].Postfix; 216 // we can checki that file exist 217 // if (NFind::DoesFileExist_Raw(path)) 218 if (!DeleteFileAlways(path)) 219 return GetLastError_noZero_HRESULT(); 220 return S_OK; 221} 222 223 224HRESULT CMultiOutStream::CloseStream_and_FinalRename(unsigned index) 225{ 226 PRF(printf("\n====== %u, CloseStream_and_FinalRename \n", index)); 227 CVolStream &s = Streams[index]; 228 // HRESULT res = S_OK; 229 bool mtime_WasSet = false; 230 if (MTime_Defined && s.Stream) 231 { 232 if (s.StreamSpec->SetMTime(&MTime)) 233 mtime_WasSet = true; 234 // else res = GetLastError_noZero_HRESULT(); 235 } 236 237 RINOK(CloseStream(index)) 238 if (s.Postfix.IsEmpty()) // if Postfix is empty, the path is already final 239 return S_OK; 240 const FString path = GetFilePath(index); 241 FString tempPath = path; 242 tempPath += s.Postfix; 243 244 if (MTime_Defined && !mtime_WasSet) 245 { 246 if (!SetDirTime(tempPath, NULL, NULL, &MTime)) 247 { 248 // res = GetLastError_noZero_HRESULT(); 249 } 250 } 251 if (!MyMoveFile(tempPath, path)) 252 return GetLastError_noZero_HRESULT(); 253 /* we clear CVolStream::Postfix. So we will not use Temp path 254 anymore for this stream, and we will work only with final path */ 255 s.Postfix.Empty(); 256 // we can ignore set_mtime error or we can return it 257 return S_OK; 258 // return res; 259} 260 261 262HRESULT CMultiOutStream::PrepareToOpenNew() 263{ 264 if (NumListItems < NumOpenFiles_AllowedMax) 265 return S_OK; 266 /* when we create zip archive: in most cases we need only starting 267 data of restricted region for rewriting zip's local header. 268 So here we close latest created volume (from Head), and we try to 269 keep oldest volumes that will be used for header rewriting later. */ 270 const int index = Head; 271 if (index == -1) 272 return E_FAIL; 273 PRF(printf("\n== %u, PrepareToOpenNew::CloseStream, NumListItems =%u \n", index, NumListItems)); 274 /* we don't expect non-restricted stream here in normal cases (if _restrict_Global was not changed). 275 if there was non-restricted stream, it should be closed before */ 276 // if (!IsRestricted_for_Close(index)) return CloseStream_and_FinalRename(index); 277 return CloseStream((unsigned)index); 278} 279 280 281HRESULT CMultiOutStream::CreateNewStream(UInt64 newSize) 282{ 283 PRF(printf("\n== %u, CreateNewStream, size =%u \n", Streams.Size(), (unsigned)newSize)); 284 285 if (Streams.Size() >= k_NumVols_MAX) 286 return E_INVALIDARG; // E_OUTOFMEMORY 287 288 RINOK(PrepareToOpenNew()) 289 CVolStream s; 290 s.StreamSpec = new COutFileStream; 291 s.Stream = s.StreamSpec; 292 const FString path = GetFilePath(Streams.Size()); 293 294 if (NFind::DoesFileExist_Raw(path)) 295 return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); 296 if (!CreateTempFile2(path, false, s.Postfix, &s.StreamSpec->File)) 297 return GetLastError_noZero_HRESULT(); 298 299 s.Start = GetGlobalOffset_for_NewStream(); 300 s.Pos = 0; 301 s.RealSize = 0; 302 303 const unsigned index = Streams.Add(s); 304 InsertToLinkedList(index); 305 306 if (newSize != 0) 307 return s.SetSize2(newSize); 308 return S_OK; 309} 310 311 312HRESULT CMultiOutStream::CreateStreams_If_Required(unsigned streamIndex) 313{ 314 // UInt64 lastStreamSize = 0; 315 for (;;) 316 { 317 const unsigned numStreamsBefore = Streams.Size(); 318 if (streamIndex < numStreamsBefore) 319 return S_OK; 320 UInt64 newSize; 321 if (streamIndex == numStreamsBefore) 322 { 323 // it's final volume that will be used for real writing. 324 /* SetSize(_offsetPos) is not required, 325 because the file Size will be set later by calling Seek() with Write() */ 326 newSize = 0; // lastStreamSize; 327 } 328 else 329 { 330 // it's intermediate volume. So we need full volume size 331 newSize = GetVolSize_for_Stream(numStreamsBefore); 332 } 333 334 RINOK(CreateNewStream(newSize)) 335 336 // optional check 337 if (numStreamsBefore + 1 != Streams.Size()) return E_FAIL; 338 339 if (streamIndex != numStreamsBefore) 340 { 341 // it's intermediate volume. So we can close it, if it's non-restricted 342 bool isRestricted; 343 { 344 const CVolStream &s = Streams[numStreamsBefore]; 345 if (newSize == 0) 346 isRestricted = IsRestricted_Empty(s); 347 else 348 isRestricted = IsRestricted(s); 349 } 350 if (!isRestricted) 351 { 352 RINOK(CloseStream_and_FinalRename(numStreamsBefore)) 353 } 354 } 355 } 356} 357 358 359HRESULT CMultiOutStream::ReOpenStream(unsigned streamIndex) 360{ 361 PRF(printf("\n====== %u, ReOpenStream \n", streamIndex)); 362 RINOK(PrepareToOpenNew()) 363 CVolStream &s = Streams[streamIndex]; 364 365 FString path = GetFilePath(streamIndex); 366 path += s.Postfix; 367 368 s.StreamSpec = new COutFileStream; 369 s.Stream = s.StreamSpec; 370 s.Pos = 0; 371 372 HRESULT hres; 373 if (s.StreamSpec->Open(path, OPEN_EXISTING)) 374 { 375 if (s.Postfix.IsEmpty()) 376 { 377 /* it's unexpected case that we open finished volume. 378 It can mean that the code for restriction is incorrect */ 379 FinalVol_WasReopen = true; 380 } 381 UInt64 realSize = 0; 382 hres = s.StreamSpec->GetSize(&realSize); 383 if (hres == S_OK) 384 { 385 if (realSize == s.RealSize) 386 { 387 InsertToLinkedList(streamIndex); 388 return S_OK; 389 } 390 // file size was changed between Close() and ReOpen() 391 // we must release Stream to be consistent with linked list 392 hres = E_FAIL; 393 } 394 } 395 else 396 hres = GetLastError_noZero_HRESULT(); 397 s.Stream.Release(); 398 s.StreamSpec = NULL; 399 return hres; 400} 401 402 403/* Sets size of stream, if new size is not equal to old size (RealSize). 404 If stream was closed and size change is required, it reopens the stream. */ 405 406HRESULT CMultiOutStream::OptReOpen_and_SetSize(unsigned index, UInt64 size) 407{ 408 CVolStream &s = Streams[index]; 409 if (size == s.RealSize) 410 return S_OK; 411 if (!s.Stream) 412 { 413 RINOK(ReOpenStream(index)) 414 } 415 PRF(printf("\n== %u, OptReOpen_and_SetSize, size =%u RealSize = %u\n", index, (unsigned)size, (unsigned)s.RealSize)); 416 // comment it to debug tail after data 417 return s.SetSize2(size); 418} 419 420 421/* 422call Normalize_finalMode(false), if _length was changed. 423 for all streams starting after _length: 424 - it sets zero size 425 - it still keeps file open 426 Note: after _length reducing with CMultiOutStream::SetSize() we can 427 have very big number of empty streams at the end of Streams[] list. 428 And Normalize_finalMode() will runs all these empty streams of Streams[] vector. 429 So it can be ineffective, if we call Normalize_finalMode() many 430 times after big reducing of (_length). 431 432call Normalize_finalMode(true) to set final presentations of all streams 433 for all streams starting after _length: 434 - it sets zero size 435 - it removes file 436 - it removes CVolStream object from Streams[] vector 437 438Note: we don't remove zero sized first volume, if (_length == 0) 439*/ 440 441HRESULT CMultiOutStream::Normalize_finalMode(bool finalMode) 442{ 443 PRF(printf("\n== Normalize_finalMode: _length =%d \n", (unsigned)_length)); 444 445 unsigned i = Streams.Size(); 446 447 UInt64 offset = 0; 448 449 /* At first we normalize (reduce or increase) the sizes of all existing 450 streams in Streams[] that can be affected by changed _length. 451 And we remove tailing zero-size streams, if (finalMode == true) */ 452 while (i != 0) 453 { 454 offset = Streams[--i].Start; // it's last item in Streams[] 455 // we don't want to remove first volume 456 if (offset < _length || i == 0) 457 { 458 const UInt64 volSize = GetVolSize_for_Stream(i); 459 UInt64 size = _length - offset; // (size != 0) here 460 if (size > volSize) 461 size = volSize; 462 RINOK(OptReOpen_and_SetSize(i, size)) 463 if (_length - offset <= volSize) 464 return S_OK; 465 // _length - offset > volSize 466 offset += volSize; 467 // _length > offset 468 break; 469 // UPDATE_HRES(res, OptReOpen_and_SetSize(i, size)); 470 } 471 472 /* we Set Size of stream to zero even for (finalMode==true), although 473 that stream will be deleted in next commands */ 474 // UPDATE_HRES(res, OptReOpen_and_SetSize(i, 0)); 475 RINOK(OptReOpen_and_SetSize(i, 0)) 476 if (finalMode) 477 { 478 RINOK(CloseStream_and_DeleteFile(i)) 479 /* CVolStream::Stream was released above already, and it was 480 removed from linked list. So we don't need to update linked list 481 structure, when we delete last item in Streams[] */ 482 Streams.DeleteBack(); 483 // Delete_LastStream_Records(); 484 } 485 } 486 487 /* now we create new zero-filled streams to cover all data up to _length */ 488 489 if (_length == 0) 490 return S_OK; 491 492 // (offset) is start offset of next stream after existing Streams[] 493 494 for (;;) 495 { 496 // _length > offset 497 const UInt64 volSize = GetVolSize_for_Stream(Streams.Size()); 498 UInt64 size = _length - offset; // (size != 0) here 499 if (size > volSize) 500 size = volSize; 501 RINOK(CreateNewStream(size)) 502 if (_length - offset <= volSize) 503 return S_OK; 504 // _length - offset > volSize) 505 offset += volSize; 506 // _length > offset 507 } 508} 509 510 511HRESULT CMultiOutStream::FinalFlush_and_CloseFiles(unsigned &numTotalVolumesRes) 512{ 513 // at first we remove unused zero-sized streams after _length 514 HRESULT res = Normalize_finalMode(true); 515 numTotalVolumesRes = Streams.Size(); 516 FOR_VECTOR (i, Streams) 517 { 518 const HRESULT res2 = CloseStream_and_FinalRename(i); 519 if (res == S_OK) 520 res = res2; 521 } 522 if (NumListItems != 0 && res == S_OK) 523 res = E_FAIL; 524 return res; 525} 526 527 528bool CMultiOutStream::SetMTime_Final(const CFiTime &mTime) 529{ 530 // we will set mtime only if new value differs from previous 531 if (!FinalVol_WasReopen && MTime_Defined && Compare_FiTime(&MTime, &mTime) == 0) 532 return true; 533 bool res = true; 534 FOR_VECTOR (i, Streams) 535 { 536 CVolStream &s = Streams[i]; 537 if (s.Stream) 538 { 539 if (!s.StreamSpec->SetMTime(&mTime)) 540 res = false; 541 } 542 else 543 { 544 if (!SetDirTime(GetFilePath(i), NULL, NULL, &mTime)) 545 res = false; 546 } 547 } 548 return res; 549} 550 551 552Z7_COM7F_IMF(CMultiOutStream::SetSize(UInt64 newSize)) 553{ 554 COM_TRY_BEGIN 555 if ((Int64)newSize < 0) 556 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; 557 if (newSize > _absLimit) 558 { 559 /* big seek value was sent to SetSize() or to Seek()+Write(). 560 It can mean one of two situations: 561 1) some incorrect code called it with big seek value. 562 2) volume size was small, and we have too big number of volumes 563 */ 564 /* in Windows SetEndOfFile() can return: 565 ERROR_NEGATIVE_SEEK: for >= (1 << 63) 566 ERROR_INVALID_PARAMETER: for > (16 TiB - 64 KiB) 567 ERROR_DISK_FULL: for <= (16 TiB - 64 KiB) 568 */ 569 // return E_FAIL; 570 // return E_OUTOFMEMORY; 571 return E_INVALIDARG; 572 } 573 574 if (newSize > _length) 575 { 576 // we don't expect such case. So we just define global restriction */ 577 _restrict_Global = newSize; 578 } 579 else if (newSize < _restrict_Global) 580 _restrict_Global = newSize; 581 582 PRF(printf("\n== SetSize, size =%u \n", (unsigned)newSize)); 583 584 _length = newSize; 585 return Normalize_finalMode(false); 586 587 COM_TRY_END 588} 589 590 591Z7_COM7F_IMF(CMultiOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)) 592{ 593 COM_TRY_BEGIN 594 if (processedSize) 595 *processedSize = 0; 596 if (size == 0) 597 return S_OK; 598 599 if (_absPos > _length) 600 { 601 // it create data only up to _absPos. 602 // but we still can need additional new streams, if _absPos at range of volume 603 RINOK(SetSize(_absPos)) 604 } 605 606 while (size != 0) 607 { 608 UInt64 volSize; 609 { 610 if (_streamIndex < Sizes.Size() - 1) 611 { 612 volSize = Sizes[_streamIndex]; 613 if (_offsetPos >= volSize) 614 { 615 _offsetPos -= volSize; 616 _streamIndex++; 617 continue; 618 } 619 } 620 else 621 { 622 volSize = Sizes[Sizes.Size() - 1]; 623 if (_offsetPos >= volSize) 624 { 625 const UInt64 v = _offsetPos / volSize; 626 if (v >= ((UInt32)(Int32)-1) - _streamIndex) 627 return E_INVALIDARG; 628 // throw 202208; 629 _streamIndex += (unsigned)v; 630 _offsetPos -= (unsigned)v * volSize; 631 } 632 if (_streamIndex >= k_NumVols_MAX) 633 return E_INVALIDARG; 634 } 635 } 636 637 // (_offsetPos < volSize) here 638 639 /* we can need to create one or more streams here, 640 vol_size for some streams is allowed to be 0. 641 Also we close some new created streams, if they are non-restricted */ 642 // file Size will be set later by calling Seek() with Write() 643 644 /* the case (_absPos > _length) was processed above with SetSize(_absPos), 645 so here it's expected. that we can create optional zero-size streams and then _streamIndex */ 646 RINOK(CreateStreams_If_Required(_streamIndex)) 647 648 CVolStream &s = Streams[_streamIndex]; 649 650 PRF(printf("\n%d, == Write : Pos = %u, RealSize = %u size =%u \n", 651 _streamIndex, (unsigned)s.Pos, (unsigned)s.RealSize, size)); 652 653 if (!s.Stream) 654 { 655 RINOK(ReOpenStream(_streamIndex)) 656 } 657 if (_offsetPos != s.Pos) 658 { 659 RINOK(s.Stream->Seek((Int64)_offsetPos, STREAM_SEEK_SET, NULL)) 660 s.Pos = _offsetPos; 661 } 662 663 UInt32 curSize = size; 664 { 665 const UInt64 rem = volSize - _offsetPos; 666 if (curSize > rem) 667 curSize = (UInt32)rem; 668 } 669 // curSize != 0 670 UInt32 realProcessed = 0; 671 672 HRESULT hres = s.Stream->Write(data, curSize, &realProcessed); 673 674 data = (const void *)((const Byte *)data + realProcessed); 675 size -= realProcessed; 676 s.Pos += realProcessed; 677 _offsetPos += realProcessed; 678 _absPos += realProcessed; 679 if (_length < _absPos) 680 _length = _absPos; 681 if (s.RealSize < _offsetPos) 682 s.RealSize = _offsetPos; 683 if (processedSize) 684 *processedSize += realProcessed; 685 686 if (s.Pos == volSize) 687 { 688 bool isRestricted; 689 if (volSize == 0) 690 isRestricted = IsRestricted_Empty(s); 691 else 692 isRestricted = IsRestricted(s); 693 if (!isRestricted) 694 { 695 const HRESULT res2 = CloseStream_and_FinalRename(_streamIndex); 696 if (hres == S_OK) 697 hres = res2; 698 } 699 _streamIndex++; 700 _offsetPos = 0; 701 } 702 703 RINOK(hres) 704 if (realProcessed == 0 && curSize != 0) 705 return E_FAIL; 706 // break; 707 } 708 return S_OK; 709 COM_TRY_END 710} 711 712 713Z7_COM7F_IMF(CMultiOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)) 714{ 715 PRF(printf("\n-- Seek seekOrigin=%u Seek =%u\n", seekOrigin, (unsigned)offset)); 716 717 switch (seekOrigin) 718 { 719 case STREAM_SEEK_SET: break; 720 case STREAM_SEEK_CUR: offset += _absPos; break; 721 case STREAM_SEEK_END: offset += _length; break; 722 default: return STG_E_INVALIDFUNCTION; 723 } 724 if (offset < 0) 725 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; 726 if ((UInt64)offset != _absPos) 727 { 728 _absPos = (UInt64)offset; 729 _offsetPos = (UInt64)offset; 730 _streamIndex = 0; 731 } 732 if (newPosition) 733 *newPosition = (UInt64)offset; 734 return S_OK; 735} 736 737 738// result value will be saturated to (UInt32)(Int32)-1 739 740unsigned CMultiOutStream::GetStreamIndex_for_Offset(UInt64 offset, UInt64 &relOffset) const 741{ 742 const unsigned last = Sizes.Size() - 1; 743 for (unsigned i = 0; i < last; i++) 744 { 745 const UInt64 size = Sizes[i]; 746 if (offset < size) 747 { 748 relOffset = offset; 749 return i; 750 } 751 offset -= size; 752 } 753 const UInt64 size = Sizes[last]; 754 const UInt64 v = offset / size; 755 if (v >= ((UInt32)(Int32)-1) - last) 756 return (UInt32)(Int32)-1; // saturation 757 relOffset = offset - (unsigned)v * size; 758 return last + (unsigned)(v); 759} 760 761 762Z7_COM7F_IMF(CMultiOutStream::SetRestriction(UInt64 begin, UInt64 end)) 763{ 764 COM_TRY_BEGIN 765 766 // begin = end = 0; // for debug 767 768 PRF(printf("\n==================== CMultiOutStream::SetRestriction %u, %u\n", (unsigned)begin, (unsigned)end)); 769 if (begin > end) 770 { 771 // these value are FAILED values. 772 return E_FAIL; 773 // return E_INVALIDARG; 774 /* 775 // or we can ignore error with 3 ways: no change, non-restricted, saturation: 776 end = begin; // non-restricted 777 end = (UInt64)(Int64)-1; // saturation: 778 return S_OK; 779 */ 780 } 781 UInt64 b = _restrict_Begin; 782 UInt64 e = _restrict_End; 783 _restrict_Begin = begin; 784 _restrict_End = end; 785 786 if (b == e) // if there were no restriction before 787 return S_OK; // no work to derestrict now. 788 789 /* [b, e) is previous restricted region. So all volumes that 790 intersect that [b, e) region are candidats for derestriction */ 791 792 if (begin != end) // if there is new non-empty restricted region 793 { 794 /* Now we will try to reduce or change (b) and (e) bounds 795 to reduce main loop that checks volumes for derestriction. 796 We still use one big derestriction region in main loop, although 797 in some cases we could have two smaller derestriction regions. 798 Also usually restriction region cannot move back from previous start position, 799 so (b <= begin) is expected here for normal cases */ 800 if (b == begin) // if same low bounds 801 b = end; // we need to derestrict only after the end of new restricted region 802 if (e == end) // if same high bounds 803 e = begin; // we need to derestrict only before the begin of new restricted region 804 } 805 806 if (b > e) // || b == (UInt64)(Int64)-1 807 return S_OK; 808 809 /* Here we close finished volumes that are not restricted anymore. 810 We close (low number) volumes at first. */ 811 812 UInt64 offset; 813 unsigned index = GetStreamIndex_for_Offset(b, offset); 814 815 for (; index < Streams.Size(); index++) 816 { 817 { 818 const CVolStream &s = Streams[index]; 819 if (_length <= s.Start) 820 break; // we don't close streams after _length 821 // (_length > s.Start) 822 const UInt64 volSize = GetVolSize_for_Stream(index); 823 if (volSize == 0) 824 { 825 if (e < s.Start) 826 break; 827 // we don't close empty stream, if next byte [s.Start, s.Start] is restricted 828 if (IsRestricted_Empty(s)) 829 continue; 830 } 831 else 832 { 833 if (e <= s.Start) 834 break; 835 // we don't close non full streams 836 if (_length - s.Start < volSize) 837 break; 838 // (volSize == s.RealSize) is expected here. So no need to check it 839 // if (volSize != s.RealSize) break; 840 if (IsRestricted(s)) 841 continue; 842 } 843 } 844 RINOK(CloseStream_and_FinalRename(index)) 845 } 846 847 return S_OK; 848 COM_TRY_END 849} 850