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
22 using namespace NWindows;
23 using namespace NFile;
24 using namespace NDir;
25
26 static 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
Destruct()34 HRESULT 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
~CMultiOutStream()94 CMultiOutStream::~CMultiOutStream()
95 {
96 // we try to avoid exception in destructors
97 Destruct();
98 }
99
100
Init(const CRecordVector<UInt64> & sizes)101 void 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 */
IsRestricted(const CVolStream & s) const155 bool 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
168 bool 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
GetFilePath(unsigned index)183 FString CMultiOutStream::GetFilePath(unsigned index)
184 {
185 FString name;
186 name.Add_UInt32((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
CloseStream(unsigned index)195 HRESULT 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
CloseStream_and_DeleteFile(unsigned index)210 HRESULT 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
CloseStream_and_FinalRename(unsigned index)224 HRESULT 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
PrepareToOpenNew()262 HRESULT CMultiOutStream::PrepareToOpenNew()
263 {
264 PRF(printf("PrepareToOpenNew NumListItems =%u, NumOpenFiles_AllowedMax = %u \n", NumListItems, NumOpenFiles_AllowedMax))
265
266 if (NumListItems < NumOpenFiles_AllowedMax)
267 return S_OK;
268 /* when we create zip archive: in most cases we need only starting
269 data of restricted region for rewriting zip's local header.
270 So here we close latest created volume (from Head), and we try to
271 keep oldest volumes that will be used for header rewriting later. */
272 const int index = Head;
273 if (index == -1)
274 return E_FAIL;
275 PRF(printf("\n== %u, PrepareToOpenNew::CloseStream, NumListItems =%u \n", index, NumListItems))
276 /* we don't expect non-restricted stream here in normal cases (if _restrict_Global was not changed).
277 if there was non-restricted stream, it should be closed before */
278 // if (!IsRestricted_for_Close(index)) return CloseStream_and_FinalRename(index);
279 return CloseStream((unsigned)index);
280 }
281
282
CreateNewStream(UInt64 newSize)283 HRESULT CMultiOutStream::CreateNewStream(UInt64 newSize)
284 {
285 PRF(printf("\n== %u, CreateNewStream, size =%u \n", Streams.Size(), (unsigned)newSize))
286
287 if (Streams.Size() >= k_NumVols_MAX)
288 return E_INVALIDARG; // E_OUTOFMEMORY
289
290 RINOK(PrepareToOpenNew())
291 CVolStream s;
292 s.StreamSpec = new COutFileStream;
293 s.Stream = s.StreamSpec;
294 const FString path = GetFilePath(Streams.Size());
295
296 if (NFind::DoesFileExist_Raw(path))
297 return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
298 if (!CreateTempFile2(path, false, s.Postfix, &s.StreamSpec->File))
299 return GetLastError_noZero_HRESULT();
300
301 s.Start = GetGlobalOffset_for_NewStream();
302 s.Pos = 0;
303 s.RealSize = 0;
304
305 const unsigned index = Streams.Add(s);
306 InsertToLinkedList(index);
307
308 if (newSize != 0)
309 return s.SetSize2(newSize);
310 return S_OK;
311 }
312
313
CreateStreams_If_Required(unsigned streamIndex)314 HRESULT CMultiOutStream::CreateStreams_If_Required(unsigned streamIndex)
315 {
316 // UInt64 lastStreamSize = 0;
317 for (;;)
318 {
319 const unsigned numStreamsBefore = Streams.Size();
320 if (streamIndex < numStreamsBefore)
321 return S_OK;
322 UInt64 newSize;
323 if (streamIndex == numStreamsBefore)
324 {
325 // it's final volume that will be used for real writing.
326 /* SetSize(_offsetPos) is not required,
327 because the file Size will be set later by calling Seek() with Write() */
328 newSize = 0; // lastStreamSize;
329 }
330 else
331 {
332 // it's intermediate volume. So we need full volume size
333 newSize = GetVolSize_for_Stream(numStreamsBefore);
334 }
335
336 RINOK(CreateNewStream(newSize))
337
338 // optional check
339 if (numStreamsBefore + 1 != Streams.Size()) return E_FAIL;
340
341 if (streamIndex != numStreamsBefore)
342 {
343 // it's intermediate volume. So we can close it, if it's non-restricted
344 bool isRestricted;
345 {
346 const CVolStream &s = Streams[numStreamsBefore];
347 if (newSize == 0)
348 isRestricted = IsRestricted_Empty(s);
349 else
350 isRestricted = IsRestricted(s);
351 }
352 if (!isRestricted)
353 {
354 RINOK(CloseStream_and_FinalRename(numStreamsBefore))
355 }
356 }
357 }
358 }
359
360
ReOpenStream(unsigned streamIndex)361 HRESULT CMultiOutStream::ReOpenStream(unsigned streamIndex)
362 {
363 PRF(printf("\n====== %u, ReOpenStream \n", streamIndex))
364 RINOK(PrepareToOpenNew())
365 CVolStream &s = Streams[streamIndex];
366
367 FString path = GetFilePath(streamIndex);
368 path += s.Postfix;
369
370 s.StreamSpec = new COutFileStream;
371 s.Stream = s.StreamSpec;
372 s.Pos = 0;
373
374 HRESULT hres;
375 if (s.StreamSpec->Open_EXISTING(path))
376 {
377 if (s.Postfix.IsEmpty())
378 {
379 /* it's unexpected case that we open finished volume.
380 It can mean that the code for restriction is incorrect */
381 FinalVol_WasReopen = true;
382 }
383 UInt64 realSize = 0;
384 hres = s.StreamSpec->GetSize(&realSize);
385 if (hres == S_OK)
386 {
387 if (realSize == s.RealSize)
388 {
389 PRF(printf("\n ReOpenStream OK realSize = %u\n", (unsigned)realSize))
390 InsertToLinkedList(streamIndex);
391 return S_OK;
392 }
393 // file size was changed between Close() and ReOpen()
394 // we must release Stream to be consistent with linked list
395 hres = E_FAIL;
396 }
397 }
398 else
399 hres = GetLastError_noZero_HRESULT();
400 s.Stream.Release();
401 s.StreamSpec = NULL;
402 return hres;
403 }
404
405
406 /* Sets size of stream, if new size is not equal to old size (RealSize).
407 If stream was closed and size change is required, it reopens the stream. */
408
OptReOpen_and_SetSize(unsigned index,UInt64 size)409 HRESULT CMultiOutStream::OptReOpen_and_SetSize(unsigned index, UInt64 size)
410 {
411 CVolStream &s = Streams[index];
412 if (size == s.RealSize)
413 return S_OK;
414 if (!s.Stream)
415 {
416 RINOK(ReOpenStream(index))
417 }
418 PRF(printf("\n== %u, OptReOpen_and_SetSize, size =%u RealSize = %u\n", index, (unsigned)size, (unsigned)s.RealSize))
419 // comment it to debug tail after data
420 return s.SetSize2(size);
421 }
422
423
424 /*
425 call Normalize_finalMode(false), if _length was changed.
426 for all streams starting after _length:
427 - it sets zero size
428 - it still keeps file open
429 Note: after _length reducing with CMultiOutStream::SetSize() we can
430 have very big number of empty streams at the end of Streams[] list.
431 And Normalize_finalMode() will runs all these empty streams of Streams[] vector.
432 So it can be ineffective, if we call Normalize_finalMode() many
433 times after big reducing of (_length).
434
435 call Normalize_finalMode(true) to set final presentations of all streams
436 for all streams starting after _length:
437 - it sets zero size
438 - it removes file
439 - it removes CVolStream object from Streams[] vector
440
441 Note: we don't remove zero sized first volume, if (_length == 0)
442 */
443
Normalize_finalMode(bool finalMode)444 HRESULT CMultiOutStream::Normalize_finalMode(bool finalMode)
445 {
446 PRF(printf("\n== Normalize_finalMode: _length =%d \n", (unsigned)_length))
447
448 unsigned i = Streams.Size();
449
450 UInt64 offset = 0;
451
452 /* At first we normalize (reduce or increase) the sizes of all existing
453 streams in Streams[] that can be affected by changed _length.
454 And we remove tailing zero-size streams, if (finalMode == true) */
455 while (i != 0)
456 {
457 offset = Streams[--i].Start; // it's last item in Streams[]
458 // we don't want to remove first volume
459 if (offset < _length || i == 0)
460 {
461 const UInt64 volSize = GetVolSize_for_Stream(i);
462 UInt64 size = _length - offset; // (size != 0) here
463 if (size > volSize)
464 size = volSize;
465 RINOK(OptReOpen_and_SetSize(i, size))
466 if (_length - offset <= volSize)
467 return S_OK;
468 // _length - offset > volSize
469 offset += volSize;
470 // _length > offset
471 break;
472 // UPDATE_HRES(res, OptReOpen_and_SetSize(i, size));
473 }
474
475 /* we Set Size of stream to zero even for (finalMode==true), although
476 that stream will be deleted in next commands */
477 // UPDATE_HRES(res, OptReOpen_and_SetSize(i, 0));
478 RINOK(OptReOpen_and_SetSize(i, 0))
479 if (finalMode)
480 {
481 RINOK(CloseStream_and_DeleteFile(i))
482 /* CVolStream::Stream was released above already, and it was
483 removed from linked list. So we don't need to update linked list
484 structure, when we delete last item in Streams[] */
485 Streams.DeleteBack();
486 // Delete_LastStream_Records();
487 }
488 }
489
490 /* now we create new zero-filled streams to cover all data up to _length */
491
492 if (_length == 0)
493 return S_OK;
494
495 // (offset) is start offset of next stream after existing Streams[]
496
497 for (;;)
498 {
499 // _length > offset
500 const UInt64 volSize = GetVolSize_for_Stream(Streams.Size());
501 UInt64 size = _length - offset; // (size != 0) here
502 if (size > volSize)
503 size = volSize;
504 RINOK(CreateNewStream(size))
505 if (_length - offset <= volSize)
506 return S_OK;
507 // _length - offset > volSize)
508 offset += volSize;
509 // _length > offset
510 }
511 }
512
513
FinalFlush_and_CloseFiles(unsigned & numTotalVolumesRes)514 HRESULT CMultiOutStream::FinalFlush_and_CloseFiles(unsigned &numTotalVolumesRes)
515 {
516 // at first we remove unused zero-sized streams after _length
517 HRESULT res = Normalize_finalMode(true);
518 numTotalVolumesRes = Streams.Size();
519 FOR_VECTOR (i, Streams)
520 {
521 const HRESULT res2 = CloseStream_and_FinalRename(i);
522 if (res == S_OK)
523 res = res2;
524 }
525 if (NumListItems != 0 && res == S_OK)
526 res = E_FAIL;
527 return res;
528 }
529
530
SetMTime_Final(const CFiTime & mTime)531 bool CMultiOutStream::SetMTime_Final(const CFiTime &mTime)
532 {
533 // we will set mtime only if new value differs from previous
534 if (!FinalVol_WasReopen && MTime_Defined && Compare_FiTime(&MTime, &mTime) == 0)
535 return true;
536 bool res = true;
537 FOR_VECTOR (i, Streams)
538 {
539 CVolStream &s = Streams[i];
540 if (s.Stream)
541 {
542 if (!s.StreamSpec->SetMTime(&mTime))
543 res = false;
544 }
545 else
546 {
547 if (!SetDirTime(GetFilePath(i), NULL, NULL, &mTime))
548 res = false;
549 }
550 }
551 return res;
552 }
553
554
Z7_COM7F_IMF(CMultiOutStream::SetSize (UInt64 newSize))555 Z7_COM7F_IMF(CMultiOutStream::SetSize(UInt64 newSize))
556 {
557 COM_TRY_BEGIN
558 if ((Int64)newSize < 0)
559 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
560 if (newSize > _absLimit)
561 {
562 /* big seek value was sent to SetSize() or to Seek()+Write().
563 It can mean one of two situations:
564 1) some incorrect code called it with big seek value.
565 2) volume size was small, and we have too big number of volumes
566 */
567 /* in Windows SetEndOfFile() can return:
568 ERROR_NEGATIVE_SEEK: for >= (1 << 63)
569 ERROR_INVALID_PARAMETER: for > (16 TiB - 64 KiB)
570 ERROR_DISK_FULL: for <= (16 TiB - 64 KiB)
571 */
572 // return E_FAIL;
573 // return E_OUTOFMEMORY;
574 return E_INVALIDARG;
575 }
576
577 if (newSize > _length)
578 {
579 // we don't expect such case. So we just define global restriction */
580 _restrict_Global = newSize;
581 }
582 else if (newSize < _restrict_Global)
583 _restrict_Global = newSize;
584
585 PRF(printf("\n== CMultiOutStream::SetSize, size =%u \n", (unsigned)newSize))
586
587 _length = newSize;
588 return Normalize_finalMode(false);
589
590 COM_TRY_END
591 }
592
593
Z7_COM7F_IMF(CMultiOutStream::Write (const void * data,UInt32 size,UInt32 * processedSize))594 Z7_COM7F_IMF(CMultiOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize))
595 {
596 COM_TRY_BEGIN
597 if (processedSize)
598 *processedSize = 0;
599 if (size == 0)
600 return S_OK;
601
602 PRF(printf("\n -- CMultiOutStream::Write() : _absPos = %6u, size =%6u \n",
603 (unsigned)_absPos, (unsigned)size))
604
605 if (_absPos > _length)
606 {
607 // it create data only up to _absPos.
608 // but we still can need additional new streams, if _absPos at range of volume
609 RINOK(SetSize(_absPos))
610 }
611
612 while (size != 0)
613 {
614 UInt64 volSize;
615 {
616 if (_streamIndex < Sizes.Size() - 1)
617 {
618 volSize = Sizes[_streamIndex];
619 if (_offsetPos >= volSize)
620 {
621 _offsetPos -= volSize;
622 _streamIndex++;
623 continue;
624 }
625 }
626 else
627 {
628 volSize = Sizes[Sizes.Size() - 1];
629 if (_offsetPos >= volSize)
630 {
631 const UInt64 v = _offsetPos / volSize;
632 if (v >= ((UInt32)(Int32)-1) - _streamIndex)
633 return E_INVALIDARG;
634 // throw 202208;
635 _streamIndex += (unsigned)v;
636 _offsetPos -= (unsigned)v * volSize;
637 }
638 if (_streamIndex >= k_NumVols_MAX)
639 return E_INVALIDARG;
640 }
641 }
642
643 // (_offsetPos < volSize) here
644
645 /* we can need to create one or more streams here,
646 vol_size for some streams is allowed to be 0.
647 Also we close some new created streams, if they are non-restricted */
648 // file Size will be set later by calling Seek() with Write()
649
650 /* the case (_absPos > _length) was processed above with SetSize(_absPos),
651 so here it's expected. that we can create optional zero-size streams and then _streamIndex */
652 RINOK(CreateStreams_If_Required(_streamIndex))
653
654 CVolStream &s = Streams[_streamIndex];
655
656 PRF(printf("\n%d, == Write : Pos = %u, RealSize = %u size =%u \n",
657 _streamIndex, (unsigned)s.Pos, (unsigned)s.RealSize, size))
658
659 if (!s.Stream)
660 {
661 RINOK(ReOpenStream(_streamIndex))
662 }
663 if (_offsetPos != s.Pos)
664 {
665 RINOK(s.Stream->Seek((Int64)_offsetPos, STREAM_SEEK_SET, NULL))
666 s.Pos = _offsetPos;
667 }
668
669 UInt32 curSize = size;
670 {
671 const UInt64 rem = volSize - _offsetPos;
672 if (curSize > rem)
673 curSize = (UInt32)rem;
674 }
675 // curSize != 0
676 UInt32 realProcessed = 0;
677
678 HRESULT hres = s.Stream->Write(data, curSize, &realProcessed);
679
680 data = (const void *)((const Byte *)data + realProcessed);
681 size -= realProcessed;
682 s.Pos += realProcessed;
683 _offsetPos += realProcessed;
684 _absPos += realProcessed;
685 if (_length < _absPos)
686 _length = _absPos;
687 if (s.RealSize < _offsetPos)
688 s.RealSize = _offsetPos;
689 if (processedSize)
690 *processedSize += realProcessed;
691
692 if (s.Pos == volSize)
693 {
694 bool isRestricted;
695 if (volSize == 0)
696 isRestricted = IsRestricted_Empty(s);
697 else
698 isRestricted = IsRestricted(s);
699 if (!isRestricted)
700 {
701 const HRESULT res2 = CloseStream_and_FinalRename(_streamIndex);
702 if (hres == S_OK)
703 hres = res2;
704 }
705 _streamIndex++;
706 _offsetPos = 0;
707 }
708
709 RINOK(hres)
710 if (realProcessed == 0 && curSize != 0)
711 return E_FAIL;
712 // break;
713 }
714 return S_OK;
715 COM_TRY_END
716 }
717
718
Z7_COM7F_IMF(CMultiOutStream::Seek (Int64 offset,UInt32 seekOrigin,UInt64 * newPosition))719 Z7_COM7F_IMF(CMultiOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
720 {
721 PRF(printf("\n-- CMultiOutStream::Seek seekOrigin=%u Seek =%u\n", seekOrigin, (unsigned)offset))
722
723 switch (seekOrigin)
724 {
725 case STREAM_SEEK_SET: break;
726 case STREAM_SEEK_CUR: offset += _absPos; break;
727 case STREAM_SEEK_END: offset += _length; break;
728 default: return STG_E_INVALIDFUNCTION;
729 }
730 if (offset < 0)
731 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
732 if ((UInt64)offset != _absPos)
733 {
734 _absPos = (UInt64)offset;
735 _offsetPos = (UInt64)offset;
736 _streamIndex = 0;
737 }
738 if (newPosition)
739 *newPosition = (UInt64)offset;
740 return S_OK;
741 }
742
743
744 // result value will be saturated to (UInt32)(Int32)-1
745
GetStreamIndex_for_Offset(UInt64 offset,UInt64 & relOffset) const746 unsigned CMultiOutStream::GetStreamIndex_for_Offset(UInt64 offset, UInt64 &relOffset) const
747 {
748 const unsigned last = Sizes.Size() - 1;
749 for (unsigned i = 0; i < last; i++)
750 {
751 const UInt64 size = Sizes[i];
752 if (offset < size)
753 {
754 relOffset = offset;
755 return i;
756 }
757 offset -= size;
758 }
759 const UInt64 size = Sizes[last];
760 const UInt64 v = offset / size;
761 if (v >= ((UInt32)(Int32)-1) - last)
762 return (unsigned)(int)-1; // saturation
763 relOffset = offset - (unsigned)v * size;
764 return last + (unsigned)(v);
765 }
766
767
Z7_COM7F_IMF(CMultiOutStream::SetRestriction (UInt64 begin,UInt64 end))768 Z7_COM7F_IMF(CMultiOutStream::SetRestriction(UInt64 begin, UInt64 end))
769 {
770 COM_TRY_BEGIN
771
772 // begin = end = 0; // for debug
773
774 PRF(printf("\n==================== CMultiOutStream::SetRestriction %u, %u\n", (unsigned)begin, (unsigned)end))
775 if (begin > end)
776 {
777 // these value are FAILED values.
778 return E_FAIL;
779 // return E_INVALIDARG;
780 /*
781 // or we can ignore error with 3 ways: no change, non-restricted, saturation:
782 end = begin; // non-restricted
783 end = (UInt64)(Int64)-1; // saturation:
784 return S_OK;
785 */
786 }
787 UInt64 b = _restrict_Begin;
788 UInt64 e = _restrict_End;
789 _restrict_Begin = begin;
790 _restrict_End = end;
791
792 if (b == e) // if there were no restriction before
793 return S_OK; // no work to derestrict now.
794
795 /* [b, e) is previous restricted region. So all volumes that
796 intersect that [b, e) region are candidats for derestriction */
797
798 if (begin != end) // if there is new non-empty restricted region
799 {
800 /* Now we will try to reduce or change (b) and (e) bounds
801 to reduce main loop that checks volumes for derestriction.
802 We still use one big derestriction region in main loop, although
803 in some cases we could have two smaller derestriction regions.
804 Also usually restriction region cannot move back from previous start position,
805 so (b <= begin) is expected here for normal cases */
806 if (b == begin) // if same low bounds
807 b = end; // we need to derestrict only after the end of new restricted region
808 if (e == end) // if same high bounds
809 e = begin; // we need to derestrict only before the begin of new restricted region
810 }
811
812 if (b > e) // || b == (UInt64)(Int64)-1
813 return S_OK;
814
815 /* Here we close finished volumes that are not restricted anymore.
816 We close (low number) volumes at first. */
817
818 UInt64 offset;
819 unsigned index = GetStreamIndex_for_Offset(b, offset);
820
821 for (; index < Streams.Size(); index++)
822 {
823 {
824 const CVolStream &s = Streams[index];
825 if (_length <= s.Start)
826 break; // we don't close streams after _length
827 // (_length > s.Start)
828 const UInt64 volSize = GetVolSize_for_Stream(index);
829 if (volSize == 0)
830 {
831 if (e < s.Start)
832 break;
833 // we don't close empty stream, if next byte [s.Start, s.Start] is restricted
834 if (IsRestricted_Empty(s))
835 continue;
836 }
837 else
838 {
839 if (e <= s.Start)
840 break;
841 // we don't close non full streams
842 if (_length - s.Start < volSize)
843 break;
844 // (volSize == s.RealSize) is expected here. So no need to check it
845 // if (volSize != s.RealSize) break;
846 if (IsRestricted(s))
847 continue;
848 }
849 }
850 RINOK(CloseStream_and_FinalRename(index))
851 }
852
853 return S_OK;
854 COM_TRY_END
855 }
856