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 | |
13 | Abstract: |
14 | |
15 | in memory stream |
16 | |
17 | |
18 | |
19 | |
20 | Revision History: |
21 | |
22 | --*/ |
23 | |
24 | #include "common.h" |
25 | |
26 | #include "objidl.h" |
27 | |
28 | class 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 | |
36 | private: |
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 | |
69 | public: |
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 | |
305 | STDAPI 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 | |