aboutsummaryrefslogtreecommitdiff
path: root/CPP/7zip/Common/MultiOutStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Common/MultiOutStream.cpp')
-rw-r--r--CPP/7zip/Common/MultiOutStream.cpp849
1 files changed, 849 insertions, 0 deletions
diff --git a/CPP/7zip/Common/MultiOutStream.cpp b/CPP/7zip/Common/MultiOutStream.cpp
new file mode 100644
index 0000000..8efb977
--- /dev/null
+++ b/CPP/7zip/Common/MultiOutStream.cpp
@@ -0,0 +1,849 @@
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}