1370b324cSopenharmony_ci// MultiOutStream.cpp
2370b324cSopenharmony_ci
3370b324cSopenharmony_ci#include "StdAfx.h"
4370b324cSopenharmony_ci
5370b324cSopenharmony_ci// #define DEBUG_VOLUMES
6370b324cSopenharmony_ci
7370b324cSopenharmony_ci#ifdef DEBUG_VOLUMES
8370b324cSopenharmony_ci#include <stdio.h>
9370b324cSopenharmony_ci  #define PRF(x) x;
10370b324cSopenharmony_ci#else
11370b324cSopenharmony_ci  #define PRF(x)
12370b324cSopenharmony_ci#endif
13370b324cSopenharmony_ci
14370b324cSopenharmony_ci#include "../../Common/ComTry.h"
15370b324cSopenharmony_ci
16370b324cSopenharmony_ci#include "../../Windows/FileDir.h"
17370b324cSopenharmony_ci#include "../../Windows/FileFind.h"
18370b324cSopenharmony_ci#include "../../Windows/System.h"
19370b324cSopenharmony_ci
20370b324cSopenharmony_ci#include "MultiOutStream.h"
21370b324cSopenharmony_ci
22370b324cSopenharmony_ciusing namespace NWindows;
23370b324cSopenharmony_ciusing namespace NFile;
24370b324cSopenharmony_ciusing namespace NDir;
25370b324cSopenharmony_ci
26370b324cSopenharmony_cistatic const unsigned k_NumVols_MAX = k_VectorSizeMax - 1;
27370b324cSopenharmony_ci      // 2; // for debug
28370b324cSopenharmony_ci
29370b324cSopenharmony_ci/*
30370b324cSopenharmony_ci#define UPDATE_HRES(hres, x) \
31370b324cSopenharmony_ci  { const HRESULT res2 = (x); if (hres == SZ_OK) hres = res2; }
32370b324cSopenharmony_ci*/
33370b324cSopenharmony_ci
34370b324cSopenharmony_ciHRESULT CMultiOutStream::Destruct()
35370b324cSopenharmony_ci{
36370b324cSopenharmony_ci  COM_TRY_BEGIN
37370b324cSopenharmony_ci  HRESULT hres = S_OK;
38370b324cSopenharmony_ci  HRESULT hres3 = S_OK;
39370b324cSopenharmony_ci
40370b324cSopenharmony_ci  while (!Streams.IsEmpty())
41370b324cSopenharmony_ci  {
42370b324cSopenharmony_ci    try
43370b324cSopenharmony_ci    {
44370b324cSopenharmony_ci      HRESULT hres2;
45370b324cSopenharmony_ci      if (NeedDelete)
46370b324cSopenharmony_ci      {
47370b324cSopenharmony_ci        /* we could call OptReOpen_and_SetSize() to test that we try to delete correct file,
48370b324cSopenharmony_ci           but we cannot guarantee that (RealSize) will be correct after Write() or another failures.
49370b324cSopenharmony_ci           And we still want to delete files even for such cases.
50370b324cSopenharmony_ci           So we don't check for OptReOpen_and_SetSize() here: */
51370b324cSopenharmony_ci        // if (OptReOpen_and_SetSize(Streams.Size() - 1, 0) == S_OK)
52370b324cSopenharmony_ci        hres2 = CloseStream_and_DeleteFile(Streams.Size() - 1);
53370b324cSopenharmony_ci      }
54370b324cSopenharmony_ci      else
55370b324cSopenharmony_ci      {
56370b324cSopenharmony_ci        hres2 = CloseStream(Streams.Size() - 1);
57370b324cSopenharmony_ci      }
58370b324cSopenharmony_ci      if (hres == S_OK)
59370b324cSopenharmony_ci        hres = hres2;
60370b324cSopenharmony_ci    }
61370b324cSopenharmony_ci    catch(...)
62370b324cSopenharmony_ci    {
63370b324cSopenharmony_ci      hres3 = E_OUTOFMEMORY;
64370b324cSopenharmony_ci    }
65370b324cSopenharmony_ci
66370b324cSopenharmony_ci    {
67370b324cSopenharmony_ci      /* Stream was released in CloseStream_*() above already, and it was removed from linked list
68370b324cSopenharmony_ci         it's some unexpected case, if Stream is still attached here.
69370b324cSopenharmony_ci         So the following code is optional: */
70370b324cSopenharmony_ci      CVolStream &s = Streams.Back();
71370b324cSopenharmony_ci      if (s.Stream)
72370b324cSopenharmony_ci      {
73370b324cSopenharmony_ci        if (hres3 == S_OK)
74370b324cSopenharmony_ci          hres3 = E_FAIL;
75370b324cSopenharmony_ci        s.Stream.Detach();
76370b324cSopenharmony_ci        /* it will be not failure, even if we call RemoveFromLinkedList()
77370b324cSopenharmony_ci           twice for same CVolStream in this Destruct() function */
78370b324cSopenharmony_ci        RemoveFromLinkedList(Streams.Size() - 1);
79370b324cSopenharmony_ci      }
80370b324cSopenharmony_ci    }
81370b324cSopenharmony_ci    Streams.DeleteBack();
82370b324cSopenharmony_ci    // Delete_LastStream_Records();
83370b324cSopenharmony_ci  }
84370b324cSopenharmony_ci
85370b324cSopenharmony_ci  if (hres == S_OK)
86370b324cSopenharmony_ci    hres = hres3;
87370b324cSopenharmony_ci  if (hres == S_OK && NumListItems != 0)
88370b324cSopenharmony_ci    hres = E_FAIL;
89370b324cSopenharmony_ci  return hres;
90370b324cSopenharmony_ci  COM_TRY_END
91370b324cSopenharmony_ci}
92370b324cSopenharmony_ci
93370b324cSopenharmony_ci
94370b324cSopenharmony_ciCMultiOutStream::~CMultiOutStream()
95370b324cSopenharmony_ci{
96370b324cSopenharmony_ci  // we try to avoid exception in destructors
97370b324cSopenharmony_ci  Destruct();
98370b324cSopenharmony_ci}
99370b324cSopenharmony_ci
100370b324cSopenharmony_ci
101370b324cSopenharmony_civoid CMultiOutStream::Init(const CRecordVector<UInt64> &sizes)
102370b324cSopenharmony_ci{
103370b324cSopenharmony_ci  Streams.Clear();
104370b324cSopenharmony_ci  InitLinkedList();
105370b324cSopenharmony_ci  Sizes = sizes;
106370b324cSopenharmony_ci  NeedDelete = true;
107370b324cSopenharmony_ci  MTime_Defined = false;
108370b324cSopenharmony_ci  FinalVol_WasReopen = false;
109370b324cSopenharmony_ci  NumOpenFiles_AllowedMax = NSystem::Get_File_OPEN_MAX_Reduced_for_3_tasks();
110370b324cSopenharmony_ci
111370b324cSopenharmony_ci  _streamIndex = 0;
112370b324cSopenharmony_ci  _offsetPos = 0;
113370b324cSopenharmony_ci  _absPos = 0;
114370b324cSopenharmony_ci  _length = 0;
115370b324cSopenharmony_ci  _absLimit = (UInt64)(Int64)-1;
116370b324cSopenharmony_ci
117370b324cSopenharmony_ci  _restrict_Begin = 0;
118370b324cSopenharmony_ci  _restrict_End = (UInt64)(Int64)-1;
119370b324cSopenharmony_ci  _restrict_Global = 0;
120370b324cSopenharmony_ci
121370b324cSopenharmony_ci  UInt64 sum = 0;
122370b324cSopenharmony_ci  unsigned i = 0;
123370b324cSopenharmony_ci  for (i = 0; i < Sizes.Size(); i++)
124370b324cSopenharmony_ci  {
125370b324cSopenharmony_ci    if (i >= k_NumVols_MAX)
126370b324cSopenharmony_ci    {
127370b324cSopenharmony_ci      _absLimit = sum;
128370b324cSopenharmony_ci      break;
129370b324cSopenharmony_ci    }
130370b324cSopenharmony_ci    const UInt64 size = Sizes[i];
131370b324cSopenharmony_ci    const UInt64 next = sum + size;
132370b324cSopenharmony_ci    if (next < sum)
133370b324cSopenharmony_ci      break;
134370b324cSopenharmony_ci    sum = next;
135370b324cSopenharmony_ci  }
136370b324cSopenharmony_ci
137370b324cSopenharmony_ci  // if (Sizes.IsEmpty()) throw "no volume sizes";
138370b324cSopenharmony_ci  const UInt64 size = Sizes.Back();
139370b324cSopenharmony_ci  if (size == 0)
140370b324cSopenharmony_ci    throw "zero size last volume";
141370b324cSopenharmony_ci
142370b324cSopenharmony_ci  if (i == Sizes.Size())
143370b324cSopenharmony_ci    if ((_absLimit - sum) / size >= (k_NumVols_MAX - i))
144370b324cSopenharmony_ci      _absLimit = sum + (k_NumVols_MAX - i) * size;
145370b324cSopenharmony_ci}
146370b324cSopenharmony_ci
147370b324cSopenharmony_ci
148370b324cSopenharmony_ci/* IsRestricted():
149370b324cSopenharmony_ci   we must call only if volume is full (s.RealSize==VolSize) or finished.
150370b324cSopenharmony_ci   the function doesn't use VolSize and it uses s.RealSize instead.
151370b324cSopenharmony_ci   it returns true  : if stream is restricted, and we can't close that stream
152370b324cSopenharmony_ci   it returns false : if there is no restriction, and we can close that stream
153370b324cSopenharmony_ci Note: (RealSize == 0) (empty volume) on restriction bounds are supposed as non-restricted
154370b324cSopenharmony_ci*/
155370b324cSopenharmony_cibool CMultiOutStream::IsRestricted(const CVolStream &s) const
156370b324cSopenharmony_ci{
157370b324cSopenharmony_ci  if (s.Start < _restrict_Global)
158370b324cSopenharmony_ci    return true;
159370b324cSopenharmony_ci  if (_restrict_Begin == _restrict_End)
160370b324cSopenharmony_ci    return false;
161370b324cSopenharmony_ci  if (_restrict_Begin <= s.Start)
162370b324cSopenharmony_ci    return _restrict_End > s.Start;
163370b324cSopenharmony_ci  return _restrict_Begin < s.Start + s.RealSize;
164370b324cSopenharmony_ci}
165370b324cSopenharmony_ci
166370b324cSopenharmony_ci/*
167370b324cSopenharmony_ci// this function check also _length and volSize
168370b324cSopenharmony_cibool CMultiOutStream::IsRestricted_for_Close(unsigned index) const
169370b324cSopenharmony_ci{
170370b324cSopenharmony_ci  const CVolStream &s = Streams[index];
171370b324cSopenharmony_ci  if (_length <= s.Start) // we don't close streams after the end, because we still can write them later
172370b324cSopenharmony_ci    return true;
173370b324cSopenharmony_ci  // (_length > s.Start)
174370b324cSopenharmony_ci  const UInt64 volSize = GetVolSize_for_Stream(index);
175370b324cSopenharmony_ci  if (volSize == 0)
176370b324cSopenharmony_ci    return IsRestricted_Empty(s);
177370b324cSopenharmony_ci  if (_length - s.Start < volSize)
178370b324cSopenharmony_ci    return true;
179370b324cSopenharmony_ci  return IsRestricted(s);
180370b324cSopenharmony_ci}
181370b324cSopenharmony_ci*/
182370b324cSopenharmony_ci
183370b324cSopenharmony_ciFString CMultiOutStream::GetFilePath(unsigned index)
184370b324cSopenharmony_ci{
185370b324cSopenharmony_ci  FString name;
186370b324cSopenharmony_ci  name.Add_UInt32(index + 1);
187370b324cSopenharmony_ci  while (name.Len() < 3)
188370b324cSopenharmony_ci    name.InsertAtFront(FTEXT('0'));
189370b324cSopenharmony_ci  name.Insert(0, Prefix);
190370b324cSopenharmony_ci  return name;
191370b324cSopenharmony_ci}
192370b324cSopenharmony_ci
193370b324cSopenharmony_ci
194370b324cSopenharmony_ci// we close stream, but we still keep item in Streams[] vector
195370b324cSopenharmony_ciHRESULT CMultiOutStream::CloseStream(unsigned index)
196370b324cSopenharmony_ci{
197370b324cSopenharmony_ci  CVolStream &s = Streams[index];
198370b324cSopenharmony_ci  if (s.Stream)
199370b324cSopenharmony_ci  {
200370b324cSopenharmony_ci    RINOK(s.StreamSpec->Close())
201370b324cSopenharmony_ci    // the following two commands must be called together:
202370b324cSopenharmony_ci    s.Stream.Release();
203370b324cSopenharmony_ci    RemoveFromLinkedList(index);
204370b324cSopenharmony_ci  }
205370b324cSopenharmony_ci  return S_OK;
206370b324cSopenharmony_ci}
207370b324cSopenharmony_ci
208370b324cSopenharmony_ci
209370b324cSopenharmony_ci// we close stream and delete file, but we still keep item in Streams[] vector
210370b324cSopenharmony_ciHRESULT CMultiOutStream::CloseStream_and_DeleteFile(unsigned index)
211370b324cSopenharmony_ci{
212370b324cSopenharmony_ci  PRF(printf("\n====== %u, CloseStream_AndDelete \n", index));
213370b324cSopenharmony_ci  RINOK(CloseStream(index))
214370b324cSopenharmony_ci  FString path = GetFilePath(index);
215370b324cSopenharmony_ci  path += Streams[index].Postfix;
216370b324cSopenharmony_ci  // we can checki that file exist
217370b324cSopenharmony_ci  // if (NFind::DoesFileExist_Raw(path))
218370b324cSopenharmony_ci  if (!DeleteFileAlways(path))
219370b324cSopenharmony_ci    return GetLastError_noZero_HRESULT();
220370b324cSopenharmony_ci  return S_OK;
221370b324cSopenharmony_ci}
222370b324cSopenharmony_ci
223370b324cSopenharmony_ci
224370b324cSopenharmony_ciHRESULT CMultiOutStream::CloseStream_and_FinalRename(unsigned index)
225370b324cSopenharmony_ci{
226370b324cSopenharmony_ci  PRF(printf("\n====== %u, CloseStream_and_FinalRename \n", index));
227370b324cSopenharmony_ci  CVolStream &s = Streams[index];
228370b324cSopenharmony_ci  // HRESULT res = S_OK;
229370b324cSopenharmony_ci  bool mtime_WasSet = false;
230370b324cSopenharmony_ci  if (MTime_Defined && s.Stream)
231370b324cSopenharmony_ci  {
232370b324cSopenharmony_ci    if (s.StreamSpec->SetMTime(&MTime))
233370b324cSopenharmony_ci      mtime_WasSet = true;
234370b324cSopenharmony_ci    // else res = GetLastError_noZero_HRESULT();
235370b324cSopenharmony_ci  }
236370b324cSopenharmony_ci
237370b324cSopenharmony_ci  RINOK(CloseStream(index))
238370b324cSopenharmony_ci  if (s.Postfix.IsEmpty()) // if Postfix is empty, the path is already final
239370b324cSopenharmony_ci    return S_OK;
240370b324cSopenharmony_ci  const FString path = GetFilePath(index);
241370b324cSopenharmony_ci  FString tempPath = path;
242370b324cSopenharmony_ci  tempPath += s.Postfix;
243370b324cSopenharmony_ci
244370b324cSopenharmony_ci  if (MTime_Defined && !mtime_WasSet)
245370b324cSopenharmony_ci  {
246370b324cSopenharmony_ci    if (!SetDirTime(tempPath, NULL, NULL, &MTime))
247370b324cSopenharmony_ci    {
248370b324cSopenharmony_ci      // res = GetLastError_noZero_HRESULT();
249370b324cSopenharmony_ci    }
250370b324cSopenharmony_ci  }
251370b324cSopenharmony_ci  if (!MyMoveFile(tempPath, path))
252370b324cSopenharmony_ci    return GetLastError_noZero_HRESULT();
253370b324cSopenharmony_ci  /* we clear CVolStream::Postfix. So we will not use Temp path
254370b324cSopenharmony_ci     anymore for this stream, and we will work only with final path */
255370b324cSopenharmony_ci  s.Postfix.Empty();
256370b324cSopenharmony_ci  // we can ignore set_mtime error or we can return it
257370b324cSopenharmony_ci  return S_OK;
258370b324cSopenharmony_ci  // return res;
259370b324cSopenharmony_ci}
260370b324cSopenharmony_ci
261370b324cSopenharmony_ci
262370b324cSopenharmony_ciHRESULT CMultiOutStream::PrepareToOpenNew()
263370b324cSopenharmony_ci{
264370b324cSopenharmony_ci  if (NumListItems < NumOpenFiles_AllowedMax)
265370b324cSopenharmony_ci    return S_OK;
266370b324cSopenharmony_ci  /* when we create zip archive: in most cases we need only starting
267370b324cSopenharmony_ci     data of restricted region for rewriting zip's local header.
268370b324cSopenharmony_ci     So here we close latest created volume (from Head), and we try to
269370b324cSopenharmony_ci     keep oldest volumes that will be used for header rewriting later. */
270370b324cSopenharmony_ci  const int index = Head;
271370b324cSopenharmony_ci  if (index == -1)
272370b324cSopenharmony_ci    return E_FAIL;
273370b324cSopenharmony_ci  PRF(printf("\n== %u, PrepareToOpenNew::CloseStream, NumListItems =%u \n", index, NumListItems));
274370b324cSopenharmony_ci  /* we don't expect non-restricted stream here in normal cases (if _restrict_Global was not changed).
275370b324cSopenharmony_ci     if there was non-restricted stream, it should be closed before */
276370b324cSopenharmony_ci  // if (!IsRestricted_for_Close(index)) return CloseStream_and_FinalRename(index);
277370b324cSopenharmony_ci  return CloseStream((unsigned)index);
278370b324cSopenharmony_ci}
279370b324cSopenharmony_ci
280370b324cSopenharmony_ci
281370b324cSopenharmony_ciHRESULT CMultiOutStream::CreateNewStream(UInt64 newSize)
282370b324cSopenharmony_ci{
283370b324cSopenharmony_ci  PRF(printf("\n== %u, CreateNewStream, size =%u \n", Streams.Size(), (unsigned)newSize));
284370b324cSopenharmony_ci
285370b324cSopenharmony_ci  if (Streams.Size() >= k_NumVols_MAX)
286370b324cSopenharmony_ci    return E_INVALIDARG; // E_OUTOFMEMORY
287370b324cSopenharmony_ci
288370b324cSopenharmony_ci  RINOK(PrepareToOpenNew())
289370b324cSopenharmony_ci  CVolStream s;
290370b324cSopenharmony_ci  s.StreamSpec = new COutFileStream;
291370b324cSopenharmony_ci  s.Stream = s.StreamSpec;
292370b324cSopenharmony_ci  const FString path = GetFilePath(Streams.Size());
293370b324cSopenharmony_ci
294370b324cSopenharmony_ci  if (NFind::DoesFileExist_Raw(path))
295370b324cSopenharmony_ci    return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
296370b324cSopenharmony_ci  if (!CreateTempFile2(path, false, s.Postfix, &s.StreamSpec->File))
297370b324cSopenharmony_ci    return GetLastError_noZero_HRESULT();
298370b324cSopenharmony_ci
299370b324cSopenharmony_ci  s.Start = GetGlobalOffset_for_NewStream();
300370b324cSopenharmony_ci  s.Pos = 0;
301370b324cSopenharmony_ci  s.RealSize = 0;
302370b324cSopenharmony_ci
303370b324cSopenharmony_ci  const unsigned index = Streams.Add(s);
304370b324cSopenharmony_ci  InsertToLinkedList(index);
305370b324cSopenharmony_ci
306370b324cSopenharmony_ci  if (newSize != 0)
307370b324cSopenharmony_ci    return s.SetSize2(newSize);
308370b324cSopenharmony_ci  return S_OK;
309370b324cSopenharmony_ci}
310370b324cSopenharmony_ci
311370b324cSopenharmony_ci
312370b324cSopenharmony_ciHRESULT CMultiOutStream::CreateStreams_If_Required(unsigned streamIndex)
313370b324cSopenharmony_ci{
314370b324cSopenharmony_ci  // UInt64 lastStreamSize = 0;
315370b324cSopenharmony_ci  for (;;)
316370b324cSopenharmony_ci  {
317370b324cSopenharmony_ci    const unsigned numStreamsBefore = Streams.Size();
318370b324cSopenharmony_ci    if (streamIndex < numStreamsBefore)
319370b324cSopenharmony_ci      return S_OK;
320370b324cSopenharmony_ci    UInt64 newSize;
321370b324cSopenharmony_ci    if (streamIndex == numStreamsBefore)
322370b324cSopenharmony_ci    {
323370b324cSopenharmony_ci      // it's final volume that will be used for real writing.
324370b324cSopenharmony_ci      /* SetSize(_offsetPos) is not required,
325370b324cSopenharmony_ci      because the file Size will be set later by calling Seek() with Write() */
326370b324cSopenharmony_ci      newSize = 0; // lastStreamSize;
327370b324cSopenharmony_ci    }
328370b324cSopenharmony_ci    else
329370b324cSopenharmony_ci    {
330370b324cSopenharmony_ci      // it's intermediate volume. So we need full volume size
331370b324cSopenharmony_ci      newSize = GetVolSize_for_Stream(numStreamsBefore);
332370b324cSopenharmony_ci    }
333370b324cSopenharmony_ci
334370b324cSopenharmony_ci    RINOK(CreateNewStream(newSize))
335370b324cSopenharmony_ci
336370b324cSopenharmony_ci    // optional check
337370b324cSopenharmony_ci    if (numStreamsBefore + 1 != Streams.Size()) return E_FAIL;
338370b324cSopenharmony_ci
339370b324cSopenharmony_ci    if (streamIndex != numStreamsBefore)
340370b324cSopenharmony_ci    {
341370b324cSopenharmony_ci      // it's intermediate volume. So we can close it, if it's non-restricted
342370b324cSopenharmony_ci      bool isRestricted;
343370b324cSopenharmony_ci      {
344370b324cSopenharmony_ci        const CVolStream &s = Streams[numStreamsBefore];
345370b324cSopenharmony_ci        if (newSize == 0)
346370b324cSopenharmony_ci          isRestricted = IsRestricted_Empty(s);
347370b324cSopenharmony_ci        else
348370b324cSopenharmony_ci          isRestricted = IsRestricted(s);
349370b324cSopenharmony_ci      }
350370b324cSopenharmony_ci      if (!isRestricted)
351370b324cSopenharmony_ci      {
352370b324cSopenharmony_ci        RINOK(CloseStream_and_FinalRename(numStreamsBefore))
353370b324cSopenharmony_ci      }
354370b324cSopenharmony_ci    }
355370b324cSopenharmony_ci  }
356370b324cSopenharmony_ci}
357370b324cSopenharmony_ci
358370b324cSopenharmony_ci
359370b324cSopenharmony_ciHRESULT CMultiOutStream::ReOpenStream(unsigned streamIndex)
360370b324cSopenharmony_ci{
361370b324cSopenharmony_ci  PRF(printf("\n====== %u, ReOpenStream \n", streamIndex));
362370b324cSopenharmony_ci  RINOK(PrepareToOpenNew())
363370b324cSopenharmony_ci  CVolStream &s = Streams[streamIndex];
364370b324cSopenharmony_ci
365370b324cSopenharmony_ci  FString path = GetFilePath(streamIndex);
366370b324cSopenharmony_ci  path += s.Postfix;
367370b324cSopenharmony_ci
368370b324cSopenharmony_ci  s.StreamSpec = new COutFileStream;
369370b324cSopenharmony_ci  s.Stream = s.StreamSpec;
370370b324cSopenharmony_ci  s.Pos = 0;
371370b324cSopenharmony_ci
372370b324cSopenharmony_ci  HRESULT hres;
373370b324cSopenharmony_ci  if (s.StreamSpec->Open(path, OPEN_EXISTING))
374370b324cSopenharmony_ci  {
375370b324cSopenharmony_ci    if (s.Postfix.IsEmpty())
376370b324cSopenharmony_ci    {
377370b324cSopenharmony_ci      /* it's unexpected case that we open finished volume.
378370b324cSopenharmony_ci         It can mean that the code for restriction is incorrect */
379370b324cSopenharmony_ci      FinalVol_WasReopen = true;
380370b324cSopenharmony_ci    }
381370b324cSopenharmony_ci    UInt64 realSize = 0;
382370b324cSopenharmony_ci    hres = s.StreamSpec->GetSize(&realSize);
383370b324cSopenharmony_ci    if (hres == S_OK)
384370b324cSopenharmony_ci    {
385370b324cSopenharmony_ci      if (realSize == s.RealSize)
386370b324cSopenharmony_ci      {
387370b324cSopenharmony_ci        InsertToLinkedList(streamIndex);
388370b324cSopenharmony_ci        return S_OK;
389370b324cSopenharmony_ci      }
390370b324cSopenharmony_ci      // file size was changed between Close() and ReOpen()
391370b324cSopenharmony_ci      // we must release Stream to be consistent with linked list
392370b324cSopenharmony_ci      hres = E_FAIL;
393370b324cSopenharmony_ci    }
394370b324cSopenharmony_ci  }
395370b324cSopenharmony_ci  else
396370b324cSopenharmony_ci    hres = GetLastError_noZero_HRESULT();
397370b324cSopenharmony_ci  s.Stream.Release();
398370b324cSopenharmony_ci  s.StreamSpec = NULL;
399370b324cSopenharmony_ci  return hres;
400370b324cSopenharmony_ci}
401370b324cSopenharmony_ci
402370b324cSopenharmony_ci
403370b324cSopenharmony_ci/* Sets size of stream, if new size is not equal to old size (RealSize).
404370b324cSopenharmony_ci   If stream was closed and size change is required, it reopens the stream. */
405370b324cSopenharmony_ci
406370b324cSopenharmony_ciHRESULT CMultiOutStream::OptReOpen_and_SetSize(unsigned index, UInt64 size)
407370b324cSopenharmony_ci{
408370b324cSopenharmony_ci  CVolStream &s = Streams[index];
409370b324cSopenharmony_ci  if (size == s.RealSize)
410370b324cSopenharmony_ci    return S_OK;
411370b324cSopenharmony_ci  if (!s.Stream)
412370b324cSopenharmony_ci  {
413370b324cSopenharmony_ci    RINOK(ReOpenStream(index))
414370b324cSopenharmony_ci  }
415370b324cSopenharmony_ci  PRF(printf("\n== %u, OptReOpen_and_SetSize, size =%u RealSize = %u\n", index, (unsigned)size, (unsigned)s.RealSize));
416370b324cSopenharmony_ci  // comment it to debug tail after data
417370b324cSopenharmony_ci  return s.SetSize2(size);
418370b324cSopenharmony_ci}
419370b324cSopenharmony_ci
420370b324cSopenharmony_ci
421370b324cSopenharmony_ci/*
422370b324cSopenharmony_cicall Normalize_finalMode(false), if _length was changed.
423370b324cSopenharmony_ci  for all streams starting after _length:
424370b324cSopenharmony_ci    - it sets zero size
425370b324cSopenharmony_ci    - it still keeps file open
426370b324cSopenharmony_ci  Note: after _length reducing with CMultiOutStream::SetSize() we can
427370b324cSopenharmony_ci    have very big number of empty streams at the end of Streams[] list.
428370b324cSopenharmony_ci    And Normalize_finalMode() will runs all these empty streams of Streams[] vector.
429370b324cSopenharmony_ci    So it can be ineffective, if we call Normalize_finalMode() many
430370b324cSopenharmony_ci    times after big reducing of (_length).
431370b324cSopenharmony_ci
432370b324cSopenharmony_cicall Normalize_finalMode(true) to set final presentations of all streams
433370b324cSopenharmony_ci  for all streams starting after _length:
434370b324cSopenharmony_ci    - it sets zero size
435370b324cSopenharmony_ci    - it removes file
436370b324cSopenharmony_ci    - it removes CVolStream object from Streams[] vector
437370b324cSopenharmony_ci
438370b324cSopenharmony_ciNote: we don't remove zero sized first volume, if (_length == 0)
439370b324cSopenharmony_ci*/
440370b324cSopenharmony_ci
441370b324cSopenharmony_ciHRESULT CMultiOutStream::Normalize_finalMode(bool finalMode)
442370b324cSopenharmony_ci{
443370b324cSopenharmony_ci  PRF(printf("\n== Normalize_finalMode: _length =%d \n", (unsigned)_length));
444370b324cSopenharmony_ci
445370b324cSopenharmony_ci  unsigned i = Streams.Size();
446370b324cSopenharmony_ci
447370b324cSopenharmony_ci  UInt64 offset = 0;
448370b324cSopenharmony_ci
449370b324cSopenharmony_ci  /* At first we normalize (reduce or increase) the sizes of all existing
450370b324cSopenharmony_ci     streams in Streams[] that can be affected by changed _length.
451370b324cSopenharmony_ci     And we remove tailing zero-size streams, if (finalMode == true) */
452370b324cSopenharmony_ci  while (i != 0)
453370b324cSopenharmony_ci  {
454370b324cSopenharmony_ci    offset = Streams[--i].Start; // it's last item in Streams[]
455370b324cSopenharmony_ci    // we don't want to remove first volume
456370b324cSopenharmony_ci    if (offset < _length || i == 0)
457370b324cSopenharmony_ci    {
458370b324cSopenharmony_ci      const UInt64 volSize = GetVolSize_for_Stream(i);
459370b324cSopenharmony_ci      UInt64 size = _length - offset; // (size != 0) here
460370b324cSopenharmony_ci      if (size > volSize)
461370b324cSopenharmony_ci        size = volSize;
462370b324cSopenharmony_ci      RINOK(OptReOpen_and_SetSize(i, size))
463370b324cSopenharmony_ci      if (_length - offset <= volSize)
464370b324cSopenharmony_ci        return S_OK;
465370b324cSopenharmony_ci      // _length - offset > volSize
466370b324cSopenharmony_ci      offset += volSize;
467370b324cSopenharmony_ci      // _length > offset
468370b324cSopenharmony_ci      break;
469370b324cSopenharmony_ci      // UPDATE_HRES(res, OptReOpen_and_SetSize(i, size));
470370b324cSopenharmony_ci    }
471370b324cSopenharmony_ci
472370b324cSopenharmony_ci    /* we Set Size of stream to zero even for (finalMode==true), although
473370b324cSopenharmony_ci       that stream will be deleted in next commands */
474370b324cSopenharmony_ci    // UPDATE_HRES(res, OptReOpen_and_SetSize(i, 0));
475370b324cSopenharmony_ci    RINOK(OptReOpen_and_SetSize(i, 0))
476370b324cSopenharmony_ci    if (finalMode)
477370b324cSopenharmony_ci    {
478370b324cSopenharmony_ci      RINOK(CloseStream_and_DeleteFile(i))
479370b324cSopenharmony_ci      /* CVolStream::Stream was released above already, and it was
480370b324cSopenharmony_ci         removed from linked list. So we don't need to update linked list
481370b324cSopenharmony_ci         structure, when we delete last item in Streams[] */
482370b324cSopenharmony_ci      Streams.DeleteBack();
483370b324cSopenharmony_ci      // Delete_LastStream_Records();
484370b324cSopenharmony_ci    }
485370b324cSopenharmony_ci  }
486370b324cSopenharmony_ci
487370b324cSopenharmony_ci  /* now we create new zero-filled streams to cover all data up to _length */
488370b324cSopenharmony_ci
489370b324cSopenharmony_ci  if (_length == 0)
490370b324cSopenharmony_ci    return S_OK;
491370b324cSopenharmony_ci
492370b324cSopenharmony_ci  // (offset) is start offset of next stream after existing Streams[]
493370b324cSopenharmony_ci
494370b324cSopenharmony_ci  for (;;)
495370b324cSopenharmony_ci  {
496370b324cSopenharmony_ci    // _length > offset
497370b324cSopenharmony_ci    const UInt64 volSize = GetVolSize_for_Stream(Streams.Size());
498370b324cSopenharmony_ci    UInt64 size = _length - offset; // (size != 0) here
499370b324cSopenharmony_ci    if (size > volSize)
500370b324cSopenharmony_ci      size = volSize;
501370b324cSopenharmony_ci    RINOK(CreateNewStream(size))
502370b324cSopenharmony_ci    if (_length - offset <= volSize)
503370b324cSopenharmony_ci      return S_OK;
504370b324cSopenharmony_ci    // _length - offset > volSize)
505370b324cSopenharmony_ci    offset += volSize;
506370b324cSopenharmony_ci    // _length > offset
507370b324cSopenharmony_ci  }
508370b324cSopenharmony_ci}
509370b324cSopenharmony_ci
510370b324cSopenharmony_ci
511370b324cSopenharmony_ciHRESULT CMultiOutStream::FinalFlush_and_CloseFiles(unsigned &numTotalVolumesRes)
512370b324cSopenharmony_ci{
513370b324cSopenharmony_ci  // at first we remove unused zero-sized streams after _length
514370b324cSopenharmony_ci  HRESULT res = Normalize_finalMode(true);
515370b324cSopenharmony_ci  numTotalVolumesRes = Streams.Size();
516370b324cSopenharmony_ci  FOR_VECTOR (i, Streams)
517370b324cSopenharmony_ci  {
518370b324cSopenharmony_ci    const HRESULT res2 = CloseStream_and_FinalRename(i);
519370b324cSopenharmony_ci    if (res == S_OK)
520370b324cSopenharmony_ci      res = res2;
521370b324cSopenharmony_ci  }
522370b324cSopenharmony_ci  if (NumListItems != 0 && res == S_OK)
523370b324cSopenharmony_ci    res = E_FAIL;
524370b324cSopenharmony_ci  return res;
525370b324cSopenharmony_ci}
526370b324cSopenharmony_ci
527370b324cSopenharmony_ci
528370b324cSopenharmony_cibool CMultiOutStream::SetMTime_Final(const CFiTime &mTime)
529370b324cSopenharmony_ci{
530370b324cSopenharmony_ci  // we will set mtime only if new value differs from previous
531370b324cSopenharmony_ci  if (!FinalVol_WasReopen && MTime_Defined && Compare_FiTime(&MTime, &mTime) == 0)
532370b324cSopenharmony_ci    return true;
533370b324cSopenharmony_ci  bool res = true;
534370b324cSopenharmony_ci  FOR_VECTOR (i, Streams)
535370b324cSopenharmony_ci  {
536370b324cSopenharmony_ci    CVolStream &s = Streams[i];
537370b324cSopenharmony_ci    if (s.Stream)
538370b324cSopenharmony_ci    {
539370b324cSopenharmony_ci      if (!s.StreamSpec->SetMTime(&mTime))
540370b324cSopenharmony_ci        res = false;
541370b324cSopenharmony_ci    }
542370b324cSopenharmony_ci    else
543370b324cSopenharmony_ci    {
544370b324cSopenharmony_ci      if (!SetDirTime(GetFilePath(i), NULL, NULL, &mTime))
545370b324cSopenharmony_ci        res = false;
546370b324cSopenharmony_ci    }
547370b324cSopenharmony_ci  }
548370b324cSopenharmony_ci  return res;
549370b324cSopenharmony_ci}
550370b324cSopenharmony_ci
551370b324cSopenharmony_ci
552370b324cSopenharmony_ciZ7_COM7F_IMF(CMultiOutStream::SetSize(UInt64 newSize))
553370b324cSopenharmony_ci{
554370b324cSopenharmony_ci  COM_TRY_BEGIN
555370b324cSopenharmony_ci  if ((Int64)newSize < 0)
556370b324cSopenharmony_ci    return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
557370b324cSopenharmony_ci  if (newSize > _absLimit)
558370b324cSopenharmony_ci  {
559370b324cSopenharmony_ci    /* big seek value was sent to SetSize() or to Seek()+Write().
560370b324cSopenharmony_ci       It can mean one of two situations:
561370b324cSopenharmony_ci         1) some incorrect code called it with big seek value.
562370b324cSopenharmony_ci         2) volume size was small, and we have too big number of volumes
563370b324cSopenharmony_ci    */
564370b324cSopenharmony_ci    /* in Windows SetEndOfFile() can return:
565370b324cSopenharmony_ci       ERROR_NEGATIVE_SEEK:     for >= (1 << 63)
566370b324cSopenharmony_ci       ERROR_INVALID_PARAMETER: for >  (16 TiB - 64 KiB)
567370b324cSopenharmony_ci       ERROR_DISK_FULL:         for <= (16 TiB - 64 KiB)
568370b324cSopenharmony_ci    */
569370b324cSopenharmony_ci    // return E_FAIL;
570370b324cSopenharmony_ci    // return E_OUTOFMEMORY;
571370b324cSopenharmony_ci    return E_INVALIDARG;
572370b324cSopenharmony_ci  }
573370b324cSopenharmony_ci
574370b324cSopenharmony_ci  if (newSize > _length)
575370b324cSopenharmony_ci  {
576370b324cSopenharmony_ci    // we don't expect such case. So we just define global restriction */
577370b324cSopenharmony_ci    _restrict_Global = newSize;
578370b324cSopenharmony_ci  }
579370b324cSopenharmony_ci  else if (newSize < _restrict_Global)
580370b324cSopenharmony_ci    _restrict_Global = newSize;
581370b324cSopenharmony_ci
582370b324cSopenharmony_ci  PRF(printf("\n== SetSize, size =%u \n", (unsigned)newSize));
583370b324cSopenharmony_ci
584370b324cSopenharmony_ci  _length = newSize;
585370b324cSopenharmony_ci  return Normalize_finalMode(false);
586370b324cSopenharmony_ci
587370b324cSopenharmony_ci  COM_TRY_END
588370b324cSopenharmony_ci}
589370b324cSopenharmony_ci
590370b324cSopenharmony_ci
591370b324cSopenharmony_ciZ7_COM7F_IMF(CMultiOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize))
592370b324cSopenharmony_ci{
593370b324cSopenharmony_ci  COM_TRY_BEGIN
594370b324cSopenharmony_ci  if (processedSize)
595370b324cSopenharmony_ci    *processedSize = 0;
596370b324cSopenharmony_ci  if (size == 0)
597370b324cSopenharmony_ci    return S_OK;
598370b324cSopenharmony_ci
599370b324cSopenharmony_ci  if (_absPos > _length)
600370b324cSopenharmony_ci  {
601370b324cSopenharmony_ci    // it create data only up to _absPos.
602370b324cSopenharmony_ci    // but we still can need additional new streams, if _absPos at range of volume
603370b324cSopenharmony_ci    RINOK(SetSize(_absPos))
604370b324cSopenharmony_ci  }
605370b324cSopenharmony_ci
606370b324cSopenharmony_ci  while (size != 0)
607370b324cSopenharmony_ci  {
608370b324cSopenharmony_ci    UInt64 volSize;
609370b324cSopenharmony_ci    {
610370b324cSopenharmony_ci      if (_streamIndex < Sizes.Size() - 1)
611370b324cSopenharmony_ci      {
612370b324cSopenharmony_ci        volSize = Sizes[_streamIndex];
613370b324cSopenharmony_ci        if (_offsetPos >= volSize)
614370b324cSopenharmony_ci        {
615370b324cSopenharmony_ci          _offsetPos -= volSize;
616370b324cSopenharmony_ci          _streamIndex++;
617370b324cSopenharmony_ci          continue;
618370b324cSopenharmony_ci        }
619370b324cSopenharmony_ci      }
620370b324cSopenharmony_ci      else
621370b324cSopenharmony_ci      {
622370b324cSopenharmony_ci        volSize = Sizes[Sizes.Size() - 1];
623370b324cSopenharmony_ci        if (_offsetPos >= volSize)
624370b324cSopenharmony_ci        {
625370b324cSopenharmony_ci          const UInt64 v = _offsetPos / volSize;
626370b324cSopenharmony_ci          if (v >= ((UInt32)(Int32)-1) - _streamIndex)
627370b324cSopenharmony_ci            return E_INVALIDARG;
628370b324cSopenharmony_ci            // throw 202208;
629370b324cSopenharmony_ci          _streamIndex += (unsigned)v;
630370b324cSopenharmony_ci          _offsetPos -= (unsigned)v * volSize;
631370b324cSopenharmony_ci        }
632370b324cSopenharmony_ci        if (_streamIndex >= k_NumVols_MAX)
633370b324cSopenharmony_ci          return E_INVALIDARG;
634370b324cSopenharmony_ci      }
635370b324cSopenharmony_ci    }
636370b324cSopenharmony_ci
637370b324cSopenharmony_ci    // (_offsetPos < volSize) here
638370b324cSopenharmony_ci
639370b324cSopenharmony_ci    /* we can need to create one or more streams here,
640370b324cSopenharmony_ci       vol_size for some streams is allowed to be 0.
641370b324cSopenharmony_ci       Also we close some new created streams, if they are non-restricted */
642370b324cSopenharmony_ci    // file Size will be set later by calling Seek() with Write()
643370b324cSopenharmony_ci
644370b324cSopenharmony_ci    /* the case (_absPos > _length) was processed above with SetSize(_absPos),
645370b324cSopenharmony_ci       so here it's expected. that we can create optional zero-size streams and then _streamIndex */
646370b324cSopenharmony_ci    RINOK(CreateStreams_If_Required(_streamIndex))
647370b324cSopenharmony_ci
648370b324cSopenharmony_ci    CVolStream &s = Streams[_streamIndex];
649370b324cSopenharmony_ci
650370b324cSopenharmony_ci    PRF(printf("\n%d, == Write : Pos = %u, RealSize = %u size =%u \n",
651370b324cSopenharmony_ci        _streamIndex, (unsigned)s.Pos, (unsigned)s.RealSize, size));
652370b324cSopenharmony_ci
653370b324cSopenharmony_ci    if (!s.Stream)
654370b324cSopenharmony_ci    {
655370b324cSopenharmony_ci      RINOK(ReOpenStream(_streamIndex))
656370b324cSopenharmony_ci    }
657370b324cSopenharmony_ci    if (_offsetPos != s.Pos)
658370b324cSopenharmony_ci    {
659370b324cSopenharmony_ci      RINOK(s.Stream->Seek((Int64)_offsetPos, STREAM_SEEK_SET, NULL))
660370b324cSopenharmony_ci      s.Pos = _offsetPos;
661370b324cSopenharmony_ci    }
662370b324cSopenharmony_ci
663370b324cSopenharmony_ci    UInt32 curSize = size;
664370b324cSopenharmony_ci    {
665370b324cSopenharmony_ci      const UInt64 rem = volSize - _offsetPos;
666370b324cSopenharmony_ci      if (curSize > rem)
667370b324cSopenharmony_ci        curSize = (UInt32)rem;
668370b324cSopenharmony_ci    }
669370b324cSopenharmony_ci    // curSize != 0
670370b324cSopenharmony_ci    UInt32 realProcessed = 0;
671370b324cSopenharmony_ci
672370b324cSopenharmony_ci    HRESULT hres = s.Stream->Write(data, curSize, &realProcessed);
673370b324cSopenharmony_ci
674370b324cSopenharmony_ci    data = (const void *)((const Byte *)data + realProcessed);
675370b324cSopenharmony_ci    size -= realProcessed;
676370b324cSopenharmony_ci    s.Pos += realProcessed;
677370b324cSopenharmony_ci    _offsetPos += realProcessed;
678370b324cSopenharmony_ci    _absPos += realProcessed;
679370b324cSopenharmony_ci    if (_length < _absPos)
680370b324cSopenharmony_ci      _length = _absPos;
681370b324cSopenharmony_ci    if (s.RealSize < _offsetPos)
682370b324cSopenharmony_ci      s.RealSize = _offsetPos;
683370b324cSopenharmony_ci    if (processedSize)
684370b324cSopenharmony_ci      *processedSize += realProcessed;
685370b324cSopenharmony_ci
686370b324cSopenharmony_ci    if (s.Pos == volSize)
687370b324cSopenharmony_ci    {
688370b324cSopenharmony_ci      bool isRestricted;
689370b324cSopenharmony_ci      if (volSize == 0)
690370b324cSopenharmony_ci        isRestricted = IsRestricted_Empty(s);
691370b324cSopenharmony_ci      else
692370b324cSopenharmony_ci        isRestricted = IsRestricted(s);
693370b324cSopenharmony_ci      if (!isRestricted)
694370b324cSopenharmony_ci      {
695370b324cSopenharmony_ci        const HRESULT res2 = CloseStream_and_FinalRename(_streamIndex);
696370b324cSopenharmony_ci        if (hres == S_OK)
697370b324cSopenharmony_ci          hres = res2;
698370b324cSopenharmony_ci      }
699370b324cSopenharmony_ci      _streamIndex++;
700370b324cSopenharmony_ci      _offsetPos = 0;
701370b324cSopenharmony_ci    }
702370b324cSopenharmony_ci
703370b324cSopenharmony_ci    RINOK(hres)
704370b324cSopenharmony_ci    if (realProcessed == 0 && curSize != 0)
705370b324cSopenharmony_ci      return E_FAIL;
706370b324cSopenharmony_ci    // break;
707370b324cSopenharmony_ci  }
708370b324cSopenharmony_ci  return S_OK;
709370b324cSopenharmony_ci  COM_TRY_END
710370b324cSopenharmony_ci}
711370b324cSopenharmony_ci
712370b324cSopenharmony_ci
713370b324cSopenharmony_ciZ7_COM7F_IMF(CMultiOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
714370b324cSopenharmony_ci{
715370b324cSopenharmony_ci  PRF(printf("\n-- Seek seekOrigin=%u Seek =%u\n", seekOrigin, (unsigned)offset));
716370b324cSopenharmony_ci
717370b324cSopenharmony_ci  switch (seekOrigin)
718370b324cSopenharmony_ci  {
719370b324cSopenharmony_ci    case STREAM_SEEK_SET: break;
720370b324cSopenharmony_ci    case STREAM_SEEK_CUR: offset += _absPos; break;
721370b324cSopenharmony_ci    case STREAM_SEEK_END: offset += _length; break;
722370b324cSopenharmony_ci    default: return STG_E_INVALIDFUNCTION;
723370b324cSopenharmony_ci  }
724370b324cSopenharmony_ci  if (offset < 0)
725370b324cSopenharmony_ci    return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
726370b324cSopenharmony_ci  if ((UInt64)offset != _absPos)
727370b324cSopenharmony_ci  {
728370b324cSopenharmony_ci    _absPos = (UInt64)offset;
729370b324cSopenharmony_ci    _offsetPos = (UInt64)offset;
730370b324cSopenharmony_ci    _streamIndex = 0;
731370b324cSopenharmony_ci  }
732370b324cSopenharmony_ci  if (newPosition)
733370b324cSopenharmony_ci    *newPosition = (UInt64)offset;
734370b324cSopenharmony_ci  return S_OK;
735370b324cSopenharmony_ci}
736370b324cSopenharmony_ci
737370b324cSopenharmony_ci
738370b324cSopenharmony_ci// result value will be saturated to (UInt32)(Int32)-1
739370b324cSopenharmony_ci
740370b324cSopenharmony_ciunsigned CMultiOutStream::GetStreamIndex_for_Offset(UInt64 offset, UInt64 &relOffset) const
741370b324cSopenharmony_ci{
742370b324cSopenharmony_ci  const unsigned last = Sizes.Size() - 1;
743370b324cSopenharmony_ci  for (unsigned i = 0; i < last; i++)
744370b324cSopenharmony_ci  {
745370b324cSopenharmony_ci    const UInt64 size = Sizes[i];
746370b324cSopenharmony_ci    if (offset < size)
747370b324cSopenharmony_ci    {
748370b324cSopenharmony_ci      relOffset = offset;
749370b324cSopenharmony_ci      return i;
750370b324cSopenharmony_ci    }
751370b324cSopenharmony_ci    offset -= size;
752370b324cSopenharmony_ci  }
753370b324cSopenharmony_ci  const UInt64 size = Sizes[last];
754370b324cSopenharmony_ci  const UInt64 v = offset / size;
755370b324cSopenharmony_ci  if (v >= ((UInt32)(Int32)-1) - last)
756370b324cSopenharmony_ci    return (UInt32)(Int32)-1; // saturation
757370b324cSopenharmony_ci  relOffset = offset - (unsigned)v * size;
758370b324cSopenharmony_ci  return last + (unsigned)(v);
759370b324cSopenharmony_ci}
760370b324cSopenharmony_ci
761370b324cSopenharmony_ci
762370b324cSopenharmony_ciZ7_COM7F_IMF(CMultiOutStream::SetRestriction(UInt64 begin, UInt64 end))
763370b324cSopenharmony_ci{
764370b324cSopenharmony_ci  COM_TRY_BEGIN
765370b324cSopenharmony_ci
766370b324cSopenharmony_ci  // begin = end = 0; // for debug
767370b324cSopenharmony_ci
768370b324cSopenharmony_ci  PRF(printf("\n==================== CMultiOutStream::SetRestriction %u, %u\n", (unsigned)begin, (unsigned)end));
769370b324cSopenharmony_ci  if (begin > end)
770370b324cSopenharmony_ci  {
771370b324cSopenharmony_ci    // these value are FAILED values.
772370b324cSopenharmony_ci    return E_FAIL;
773370b324cSopenharmony_ci    // return E_INVALIDARG;
774370b324cSopenharmony_ci    /*
775370b324cSopenharmony_ci    // or we can ignore error with 3 ways: no change, non-restricted, saturation:
776370b324cSopenharmony_ci    end = begin;             // non-restricted
777370b324cSopenharmony_ci    end = (UInt64)(Int64)-1; // saturation:
778370b324cSopenharmony_ci    return S_OK;
779370b324cSopenharmony_ci    */
780370b324cSopenharmony_ci  }
781370b324cSopenharmony_ci  UInt64 b = _restrict_Begin;
782370b324cSopenharmony_ci  UInt64 e = _restrict_End;
783370b324cSopenharmony_ci  _restrict_Begin = begin;
784370b324cSopenharmony_ci  _restrict_End = end;
785370b324cSopenharmony_ci
786370b324cSopenharmony_ci  if (b == e)    // if there were no restriction before
787370b324cSopenharmony_ci    return S_OK; // no work to derestrict now.
788370b324cSopenharmony_ci
789370b324cSopenharmony_ci  /* [b, e) is previous restricted region. So all volumes that
790370b324cSopenharmony_ci     intersect that [b, e) region are candidats for derestriction */
791370b324cSopenharmony_ci
792370b324cSopenharmony_ci  if (begin != end) // if there is new non-empty restricted region
793370b324cSopenharmony_ci  {
794370b324cSopenharmony_ci    /* Now we will try to reduce or change (b) and (e) bounds
795370b324cSopenharmony_ci       to reduce main loop that checks volumes for derestriction.
796370b324cSopenharmony_ci       We still use one big derestriction region in main loop, although
797370b324cSopenharmony_ci       in some cases we could have two smaller derestriction regions.
798370b324cSopenharmony_ci       Also usually restriction region cannot move back from previous start position,
799370b324cSopenharmony_ci       so (b <= begin) is expected here for normal cases */
800370b324cSopenharmony_ci    if (b == begin) // if same low bounds
801370b324cSopenharmony_ci      b = end;      // we need to derestrict only after the end of new restricted region
802370b324cSopenharmony_ci    if (e == end)   // if same high bounds
803370b324cSopenharmony_ci      e = begin;    // we need to derestrict only before the begin of new restricted region
804370b324cSopenharmony_ci  }
805370b324cSopenharmony_ci
806370b324cSopenharmony_ci  if (b > e) //  || b == (UInt64)(Int64)-1
807370b324cSopenharmony_ci    return S_OK;
808370b324cSopenharmony_ci
809370b324cSopenharmony_ci  /* Here we close finished volumes that are not restricted anymore.
810370b324cSopenharmony_ci     We close (low number) volumes at first. */
811370b324cSopenharmony_ci
812370b324cSopenharmony_ci  UInt64 offset;
813370b324cSopenharmony_ci  unsigned index = GetStreamIndex_for_Offset(b, offset);
814370b324cSopenharmony_ci
815370b324cSopenharmony_ci  for (; index < Streams.Size(); index++)
816370b324cSopenharmony_ci  {
817370b324cSopenharmony_ci    {
818370b324cSopenharmony_ci      const CVolStream &s = Streams[index];
819370b324cSopenharmony_ci      if (_length <= s.Start)
820370b324cSopenharmony_ci        break; // we don't close streams after _length
821370b324cSopenharmony_ci      // (_length > s.Start)
822370b324cSopenharmony_ci      const UInt64 volSize = GetVolSize_for_Stream(index);
823370b324cSopenharmony_ci      if (volSize == 0)
824370b324cSopenharmony_ci      {
825370b324cSopenharmony_ci        if (e < s.Start)
826370b324cSopenharmony_ci          break;
827370b324cSopenharmony_ci        // we don't close empty stream, if next byte [s.Start, s.Start] is restricted
828370b324cSopenharmony_ci        if (IsRestricted_Empty(s))
829370b324cSopenharmony_ci          continue;
830370b324cSopenharmony_ci      }
831370b324cSopenharmony_ci      else
832370b324cSopenharmony_ci      {
833370b324cSopenharmony_ci        if (e <= s.Start)
834370b324cSopenharmony_ci          break;
835370b324cSopenharmony_ci        // we don't close non full streams
836370b324cSopenharmony_ci        if (_length - s.Start < volSize)
837370b324cSopenharmony_ci          break;
838370b324cSopenharmony_ci        // (volSize == s.RealSize) is expected here. So no need to check it
839370b324cSopenharmony_ci        // if (volSize != s.RealSize) break;
840370b324cSopenharmony_ci        if (IsRestricted(s))
841370b324cSopenharmony_ci          continue;
842370b324cSopenharmony_ci      }
843370b324cSopenharmony_ci    }
844370b324cSopenharmony_ci    RINOK(CloseStream_and_FinalRename(index))
845370b324cSopenharmony_ci  }
846370b324cSopenharmony_ci
847370b324cSopenharmony_ci  return S_OK;
848370b324cSopenharmony_ci  COM_TRY_END
849370b324cSopenharmony_ci}
850