1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#define WIN32_LEAN_AND_MEAN
11#include <Windows.h>
12#include <ole2.h>
13#include "SkIStream.h"
14#include "SkStream.h"
15
16/**
17 * SkBaseIStream
18 */
19SkBaseIStream::SkBaseIStream() : _refcount(1) { }
20SkBaseIStream::~SkBaseIStream() { }
21
22HRESULT STDMETHODCALLTYPE SkBaseIStream::QueryInterface(REFIID iid
23                                                      , void ** ppvObject)
24{
25    if (NULL == ppvObject) {
26        return E_INVALIDARG;
27    }
28    if (iid == __uuidof(IUnknown)
29        || iid == __uuidof(IStream)
30        || iid == __uuidof(ISequentialStream))
31    {
32        *ppvObject = static_cast<IStream*>(this);
33        AddRef();
34        return S_OK;
35    } else {
36        *ppvObject = NULL;
37        return E_NOINTERFACE;
38    }
39}
40
41ULONG STDMETHODCALLTYPE SkBaseIStream::AddRef(void) {
42    return (ULONG)InterlockedIncrement(&_refcount);
43}
44
45ULONG STDMETHODCALLTYPE SkBaseIStream::Release(void) {
46    ULONG res = (ULONG) InterlockedDecrement(&_refcount);
47    if (0 == res) {
48        delete this;
49    }
50    return res;
51}
52
53// ISequentialStream Interface
54HRESULT STDMETHODCALLTYPE SkBaseIStream::Read(void* pv
55                                            , ULONG cb
56                                            , ULONG* pcbRead)
57{ return E_NOTIMPL; }
58
59HRESULT STDMETHODCALLTYPE SkBaseIStream::Write(void const* pv
60                                             , ULONG cb
61                                             , ULONG* pcbWritten)
62{ return E_NOTIMPL; }
63
64// IStream Interface
65HRESULT STDMETHODCALLTYPE SkBaseIStream::SetSize(ULARGE_INTEGER)
66{ return E_NOTIMPL; }
67
68HRESULT STDMETHODCALLTYPE SkBaseIStream::CopyTo(IStream*
69                                              , ULARGE_INTEGER
70                                              , ULARGE_INTEGER*
71                                              , ULARGE_INTEGER*)
72{ return E_NOTIMPL; }
73
74HRESULT STDMETHODCALLTYPE SkBaseIStream::Commit(DWORD)
75{ return E_NOTIMPL; }
76
77HRESULT STDMETHODCALLTYPE SkBaseIStream::Revert(void)
78{ return E_NOTIMPL; }
79
80HRESULT STDMETHODCALLTYPE SkBaseIStream::LockRegion(ULARGE_INTEGER
81                                                  , ULARGE_INTEGER
82                                                  , DWORD)
83{ return E_NOTIMPL; }
84
85HRESULT STDMETHODCALLTYPE SkBaseIStream::UnlockRegion(ULARGE_INTEGER
86                                                    , ULARGE_INTEGER
87                                                    , DWORD)
88{ return E_NOTIMPL; }
89
90HRESULT STDMETHODCALLTYPE SkBaseIStream::Clone(IStream **)
91{ return E_NOTIMPL; }
92
93HRESULT STDMETHODCALLTYPE SkBaseIStream::Seek(LARGE_INTEGER liDistanceToMove
94                                            , DWORD dwOrigin
95                                            , ULARGE_INTEGER* lpNewFilePointer)
96{ return E_NOTIMPL; }
97
98HRESULT STDMETHODCALLTYPE SkBaseIStream::Stat(STATSTG* pStatstg
99                                            , DWORD grfStatFlag)
100{ return E_NOTIMPL; }
101
102
103/**
104 * SkIStream
105 */
106SkIStream::SkIStream(SkStream* stream, bool unrefOnRelease)
107    : SkBaseIStream()
108    , fSkStream(stream)
109    , fUnrefOnRelease(unrefOnRelease)
110    , fLocation()
111{
112    this->fSkStream->rewind();
113}
114
115SkIStream::~SkIStream() {
116    if (NULL != this->fSkStream && fUnrefOnRelease) {
117        this->fSkStream->unref();
118    }
119}
120
121HRESULT SkIStream::CreateFromSkStream(SkStream* stream
122                                    , bool unrefOnRelease
123                                    , IStream ** ppStream)
124{
125    *ppStream = new SkIStream(stream, unrefOnRelease);
126    return S_OK;
127}
128
129// ISequentialStream Interface
130HRESULT STDMETHODCALLTYPE SkIStream::Read(void* pv, ULONG cb, ULONG* pcbRead) {
131    *pcbRead = static_cast<ULONG>(this->fSkStream->read(pv, cb));
132    this->fLocation.QuadPart += *pcbRead;
133    return (*pcbRead == cb) ? S_OK : S_FALSE;
134}
135
136HRESULT STDMETHODCALLTYPE SkIStream::Write(void const* pv
137                                         , ULONG cb
138                                         , ULONG* pcbWritten)
139{
140    return STG_E_CANTSAVE;
141}
142
143// IStream Interface
144HRESULT STDMETHODCALLTYPE SkIStream::Seek(LARGE_INTEGER liDistanceToMove
145                                        , DWORD dwOrigin
146                                        , ULARGE_INTEGER* lpNewFilePointer)
147{
148    HRESULT hr = S_OK;
149
150    switch(dwOrigin) {
151    case STREAM_SEEK_SET: {
152        if (!this->fSkStream->rewind()) {
153            hr = E_FAIL;
154        } else {
155            size_t skipped = this->fSkStream->skip(
156                static_cast<size_t>(liDistanceToMove.QuadPart)
157            );
158            this->fLocation.QuadPart = skipped;
159            if (skipped != liDistanceToMove.QuadPart) {
160                hr = E_FAIL;
161            }
162        }
163        break;
164    }
165    case STREAM_SEEK_CUR: {
166        size_t skipped = this->fSkStream->skip(
167            static_cast<size_t>(liDistanceToMove.QuadPart)
168        );
169        this->fLocation.QuadPart += skipped;
170        if (skipped != liDistanceToMove.QuadPart) {
171            hr = E_FAIL;
172        }
173        break;
174    }
175    case STREAM_SEEK_END: {
176        if (!this->fSkStream->rewind()) {
177            hr = E_FAIL;
178        } else {
179            LONGLONG skip = this->fSkStream->getLength()
180                          + liDistanceToMove.QuadPart;
181            size_t skipped = this->fSkStream->skip(static_cast<size_t>(skip));
182            this->fLocation.QuadPart = skipped;
183            if (skipped != skip) {
184                hr = E_FAIL;
185            }
186        }
187        break;
188    }
189    default:
190        hr = STG_E_INVALIDFUNCTION;
191        break;
192    }
193
194    if (NULL != lpNewFilePointer) {
195        lpNewFilePointer->QuadPart = this->fLocation.QuadPart;
196    }
197    return hr;
198}
199
200HRESULT STDMETHODCALLTYPE SkIStream::Stat(STATSTG* pStatstg
201                                        , DWORD grfStatFlag)
202{
203    if (0 == (grfStatFlag & STATFLAG_NONAME)) {
204        return STG_E_INVALIDFLAG;
205    }
206    pStatstg->pwcsName = NULL;
207    pStatstg->cbSize.QuadPart = this->fSkStream->getLength();
208    pStatstg->clsid = CLSID_NULL;
209    pStatstg->type = STGTY_STREAM;
210    pStatstg->grfMode = STGM_READ;
211    return S_OK;
212}
213
214
215/**
216 * SkIWStream
217 */
218SkWIStream::SkWIStream(SkWStream* stream)
219    : SkBaseIStream()
220    , fSkWStream(stream)
221{ }
222
223SkWIStream::~SkWIStream() {
224    if (NULL != this->fSkWStream) {
225        this->fSkWStream->flush();
226    }
227}
228
229HRESULT SkWIStream::CreateFromSkWStream(SkWStream* stream
230                                      , IStream ** ppStream)
231{
232    *ppStream = new SkWIStream(stream);
233    return S_OK;
234}
235
236// ISequentialStream Interface
237HRESULT STDMETHODCALLTYPE SkWIStream::Write(void const* pv
238                                          , ULONG cb
239                                          , ULONG* pcbWritten)
240{
241    HRESULT hr = S_OK;
242    bool wrote = this->fSkWStream->write(pv, cb);
243    if (wrote) {
244        *pcbWritten = cb;
245    } else {
246        *pcbWritten = 0;
247        hr = S_FALSE;
248    }
249    return hr;
250}
251
252// IStream Interface
253HRESULT STDMETHODCALLTYPE SkWIStream::Commit(DWORD) {
254    this->fSkWStream->flush();
255    return S_OK;
256}
257
258HRESULT STDMETHODCALLTYPE SkWIStream::Stat(STATSTG* pStatstg
259                                         , DWORD grfStatFlag)
260{
261    if (0 == (grfStatFlag & STATFLAG_NONAME)) {
262        return STG_E_INVALIDFLAG;
263    }
264    pStatstg->pwcsName = NULL;
265    pStatstg->cbSize.QuadPart = 0;
266    pStatstg->clsid = CLSID_NULL;
267    pStatstg->type = STGTY_STREAM;
268    pStatstg->grfMode = STGM_WRITE;
269    return S_OK;
270}
271