1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5
6//
7// ===========================================================================
8// File: memorystream.cpp
9//
10// ===========================================================================
11/*++
12
13Abstract:
14
15 in memory stream
16
17
18
19
20Revision History:
21
22--*/
23
24#include "common.h"
25
26#include "objidl.h"
27
28class MemoryStream : public IStream
29{
30 LONG m_cRef; // QI refcount
31 ULONG m_nPos; // the current position in the stream
32 ULONG m_nSize; // the current size of the stream
33 ULONG m_nData; // the size of the allocated data storage, can be < m_nSize
34 BYTE* m_pData; // the data storage
35
36private:
37 HRESULT Ensure(ULONG nNewData)
38 {
39 if (nNewData > m_nData)
40 {
41 // apply some heurestic for growing
42 ULONG n = m_nData;
43
44 // grow 2x for smaller sizes, 1.25x for bigger sizes
45 n = min(2 * n, n + n / 4 + 0x100000);
46
47 // don't allocate tiny chunks
48 n = max(n, 0x100);
49
50 // compare with the hard limit
51 nNewData = max(n, nNewData);
52 }
53 else
54 if (nNewData > m_nData / 4)
55 {
56 // shrinking but it is not worth it
57 return S_OK;
58 }
59
60 BYTE * pNewData = (BYTE*)realloc(m_pData, nNewData);
61 if (pNewData == NULL && nNewData != 0)
62 return E_OUTOFMEMORY;
63
64 m_nData = nNewData;
65 m_pData = pNewData;
66 return S_OK;
67 }
68
69public:
70 MemoryStream()
71 {
72 m_cRef = 1;
73 m_nPos = 0;
74 m_nSize = 0;
75 m_nData = 0;
76 m_pData = NULL;
77 }
78
79#ifdef __GNUC__
80 virtual
81#endif
82 ~MemoryStream()
83 {
84 free(m_pData);
85 }
86
87 HRESULT STDMETHODCALLTYPE QueryInterface(
88 REFIID riid,
89 void **ppvObject)
90 {
91 if (riid == IID_IStream ||
92 riid == IID_ISequentialStream ||
93 riid == IID_IUnknown)
94 {
95 InterlockedIncrement(&m_cRef);
96 *ppvObject = this;
97 return S_OK;
98 }
99 else
100 {
101 *ppvObject = NULL;
102 return E_NOINTERFACE;
103 }
104 }
105
106 ULONG STDMETHODCALLTYPE AddRef()
107 {
108 return InterlockedIncrement(&m_cRef);
109 }
110
111 ULONG STDMETHODCALLTYPE Release()
112 {
113 LONG cRef = InterlockedDecrement(&m_cRef);
114 if (cRef == 0)
115 delete this;
116 return cRef;
117 }
118
119 HRESULT STDMETHODCALLTYPE Read(
120 void *pv,
121 ULONG cb,
122 ULONG *pcbRead)
123 {
124 ULONG nData;
125 ULONG nNewPos = m_nPos + cb;
126
127 // check for overflow
128 if (nNewPos < cb)
129 return STG_E_INVALIDFUNCTION;
130
131 // compare with the actual size
132 nNewPos = min(nNewPos, m_nSize);
133
134 // compare with the data available
135 nData = min(nNewPos, m_nData);
136
137 // copy the data over
138 if (nData > m_nPos)
139 memcpy(pv, m_pData + m_nPos, nData - m_nPos);
140
141 // fill the rest with zeros
142 if (nNewPos > nData)
143 memset((BYTE*)pv + (nData - m_nPos), 0, nNewPos - nData);
144
145 cb = nNewPos - m_nPos;
146 m_nPos = nNewPos;
147
148 if (pcbRead)
149 *pcbRead = cb;
150
151 return S_OK;
152 }
153
154 HRESULT STDMETHODCALLTYPE Write(
155 const void *pv,
156 ULONG cb,
157 ULONG *pcbWritten)
158 {
159 ULONG nNewPos = m_nPos + cb;
160
161 // check for overflow
162 if (nNewPos < cb)
163 return STG_E_INVALIDFUNCTION;
164
165 // ensure the space
166 if (nNewPos > m_nData)
167 {
168 HRESULT hr = Ensure(nNewPos);
169 if (FAILED(hr)) return hr;
170 }
171
172 // copy the data over
173 memcpy(m_pData + m_nPos, pv, cb);
174
175 m_nPos = nNewPos;
176 if (m_nPos > m_nSize)
177 m_nSize = m_nPos;
178
179 if (pcbWritten)
180 *pcbWritten = cb;
181
182 return S_OK;
183 }
184
185 HRESULT STDMETHODCALLTYPE Seek(
186 LARGE_INTEGER dlibMove,
187 DWORD dwOrigin,
188 ULARGE_INTEGER *plibNewPosition)
189 {
190 ULONG lStartPos;
191 LONGLONG lNewPos;
192
193 switch (dwOrigin)
194 {
195 case STREAM_SEEK_SET:
196 lStartPos = 0;
197 break;
198 case STREAM_SEEK_CUR:
199 lStartPos = m_nPos;
200 break;
201 case STREAM_SEEK_END:
202 lStartPos = m_nSize;
203 break;
204 default:
205 return STG_E_INVALIDFUNCTION;
206 }
207
208 lNewPos = lStartPos + dlibMove.QuadPart;
209
210 // it is an error to seek before the beginning of the stream
211 if (lNewPos < 0)
212 return STG_E_INVALIDFUNCTION;
213
214 // It is not, however, an error to seek past the end of the stream
215 if (lNewPos > m_nSize)
216 {
217 ULARGE_INTEGER NewSize;
218 NewSize.QuadPart = lNewPos;
219
220 HRESULT hr = SetSize(NewSize);
221 if (FAILED(hr)) return hr;
222 }
223
224 m_nPos = (ULONG)lNewPos;
225
226 if (plibNewPosition != NULL)
227 plibNewPosition->QuadPart = m_nPos;
228
229 return S_OK;
230 }
231
232 HRESULT STDMETHODCALLTYPE SetSize(
233 ULARGE_INTEGER libNewSize)
234 {
235 if (libNewSize.u.HighPart != 0)
236 return STG_E_INVALIDFUNCTION;
237
238 m_nSize = libNewSize.u.LowPart;
239
240 // free the space if we are shrinking
241 if (m_nSize < m_nData)
242 Ensure(m_nSize);
243
244 return S_OK;
245 }
246
247 HRESULT STDMETHODCALLTYPE CopyTo(
248 IStream *pstm,
249 ULARGE_INTEGER cb,
250 ULARGE_INTEGER *pcbRead,
251 ULARGE_INTEGER *pcbWritten)
252 {
253 _ASSERTE(false);
254 return E_NOTIMPL;
255 }
256
257 HRESULT STDMETHODCALLTYPE Commit(
258 DWORD grfCommitFlags)
259 {
260 _ASSERTE(false);
261 return E_NOTIMPL;
262 }
263
264 HRESULT STDMETHODCALLTYPE Revert()
265 {
266 _ASSERTE(false);
267 return E_NOTIMPL;
268 }
269
270 HRESULT STDMETHODCALLTYPE LockRegion(
271 ULARGE_INTEGER libOffset,
272 ULARGE_INTEGER cb,
273 DWORD dwLockType)
274 {
275 _ASSERTE(false);
276 return E_NOTIMPL;
277 }
278
279 HRESULT STDMETHODCALLTYPE UnlockRegion(
280 ULARGE_INTEGER libOffset,
281 ULARGE_INTEGER cb,
282 DWORD dwLockType)
283 {
284 _ASSERTE(false);
285 return E_NOTIMPL;
286 }
287
288 HRESULT STDMETHODCALLTYPE Stat(
289 STATSTG *pstatstg,
290 DWORD grfStatFlag)
291 {
292 memset(pstatstg, 0, sizeof(STATSTG));
293 pstatstg->cbSize.QuadPart = m_nSize;
294 return S_OK;
295 }
296
297 HRESULT STDMETHODCALLTYPE Clone(
298 IStream **ppstm)
299 {
300 _ASSERTE(false);
301 return E_NOTIMPL;
302 }
303};
304
305STDAPI CreateStreamOnHGlobal(PVOID hGlobal, BOOL fDeleteOnRelease, IStream** ppstm)
306{
307 MemoryStream* pStream;
308
309 if (hGlobal != NULL) return E_NOTIMPL;
310 _ASSERTE(fDeleteOnRelease == TRUE);
311
312 pStream = new MemoryStream;
313 if (pStream == NULL) return E_OUTOFMEMORY;
314
315 *ppstm = pStream;
316 return S_OK;
317}
318