summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Common/StreamBinder.cpp
blob: 7a4c0ed26ad55c0c9496dae04a4d3a9ce58da4f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// StreamBinder.cpp

#include "StdAfx.h"

#include "../../Common/MyCom.h"

#include "StreamBinder.h"

class CBinderInStream:
  public ISequentialInStream,
  public CMyUnknownImp
{
  CStreamBinder *_binder;
public:
  MY_UNKNOWN_IMP
  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
  ~CBinderInStream() { _binder->CloseRead(); }
  CBinderInStream(CStreamBinder *binder): _binder(binder) {}
};

STDMETHODIMP CBinderInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
  { return _binder->Read(data, size, processedSize); }

class CBinderOutStream:
  public ISequentialOutStream,
  public CMyUnknownImp
{
  CStreamBinder *_binder;
public:
  MY_UNKNOWN_IMP
  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
  ~CBinderOutStream() { _binder->CloseWrite(); }
  CBinderOutStream(CStreamBinder *binder): _binder(binder) {}
};

STDMETHODIMP CBinderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
  { return _binder->Write(data, size, processedSize); }



WRes CStreamBinder::CreateEvents()
{
  RINOK(_canWrite_Event.Create(true));
  RINOK(_canRead_Event.Create());
  return _readingWasClosed_Event.Create();
}

void CStreamBinder::ReInit()
{
  _waitWrite = true;
  _canRead_Event.Reset();
  _readingWasClosed_Event.Reset();
  ProcessedSize = 0;
}


void CStreamBinder::CreateStreams(ISequentialInStream **inStream, ISequentialOutStream **outStream)
{
  _waitWrite = true;
  _bufSize = 0;
  _buf = NULL;
  ProcessedSize = 0;

  CBinderInStream *inStreamSpec = new CBinderInStream(this);
  CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
  *inStream = inStreamLoc.Detach();

  CBinderOutStream *outStreamSpec = new CBinderOutStream(this);
  CMyComPtr<ISequentialOutStream> outStreamLoc(outStreamSpec);
  *outStream = outStreamLoc.Detach();
}

// (_canRead_Event && _bufSize == 0) means that stream is finished.

HRESULT CStreamBinder::Read(void *data, UInt32 size, UInt32 *processedSize)
{
  if (processedSize)
    *processedSize = 0;
  if (size != 0)
  {
    if (_waitWrite)
    {
      RINOK(_canRead_Event.Lock());
      _waitWrite = false;
    }
    if (size > _bufSize)
      size = _bufSize;
    if (size != 0)
    {
      memcpy(data, _buf, size);
      _buf = ((const Byte *)_buf) + size;
      ProcessedSize += size;
      if (processedSize)
        *processedSize = size;
      _bufSize -= size;
      if (_bufSize == 0)
      {
        _waitWrite = true;
        _canRead_Event.Reset();
        _canWrite_Event.Set();
      }
    }
  }
  return S_OK;
}

HRESULT CStreamBinder::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
  if (processedSize)
    *processedSize = 0;
  if (size != 0)
  {
    _buf = data;
    _bufSize = size;
    _canWrite_Event.Reset();
    _canRead_Event.Set();

    HANDLE events[2] = { _canWrite_Event, _readingWasClosed_Event };
    DWORD waitResult = ::WaitForMultipleObjects(2, events, FALSE, INFINITE);
    if (waitResult != WAIT_OBJECT_0 + 0)
      return S_FALSE;
    if (processedSize)
      *processedSize = size;
  }
  return S_OK;
}