1//
2// PartialStream.cpp
3//
4// Library: Zip
5// Package: Zip
6// Module: PartialStream
7//
8// Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Zip/PartialStream.h"
16#include "Poco/Exception.h"
17#include <cstring>
18
19
20namespace Poco {
21namespace Zip {
22
23
24PartialStreamBuf::PartialStreamBuf(std::istream& in, std::ios::pos_type start, std::ios::pos_type end, const std::string& pre, const std::string& post, bool initStream):
25 Poco::BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in),
26 _initialized(!initStream),
27 _start(start),
28 _numBytes(end-start),
29 _bytesWritten(0),
30 _pIstr(&in),
31 _pOstr(0),
32 _prefix(pre),
33 _postfix(post),
34 _ignoreStart(0),
35 _buffer(0),
36 _bufferOffset(0)
37{
38}
39
40
41PartialStreamBuf::PartialStreamBuf(std::ostream& out, std::size_t start, std::size_t end, bool initStream):
42 Poco::BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::out),
43 _initialized(!initStream),
44 _start(0),
45 _numBytes(0),
46 _bytesWritten(0),
47 _pIstr(0),
48 _pOstr(&out),
49 _ignoreStart(start),
50 _buffer(end),
51 _bufferOffset(0)
52{
53}
54
55
56PartialStreamBuf::~PartialStreamBuf()
57{
58}
59
60
61int PartialStreamBuf::readFromDevice(char* buffer, std::streamsize length)
62{
63 if (_pIstr == 0 ||length == 0) return -1;
64 if (!_initialized)
65 {
66 _initialized = true;
67 _pIstr->clear();
68 _pIstr->seekg(_start, std::ios_base::beg);
69 if (_pIstr->fail())
70 throw Poco::IOException("Failed to seek on input stream");
71 }
72 if (!_prefix.empty())
73 {
74 std::streamsize tmp = (_prefix.size() > length)? length: static_cast<std::streamsize>(_prefix.size());
75 std::memcpy(buffer, _prefix.c_str(), tmp);
76 _prefix = _prefix.substr(tmp);
77 return static_cast<int>(tmp);
78 }
79
80 if (_numBytes == 0)
81 {
82 if (!_postfix.empty())
83 {
84 std::streamsize tmp = (_postfix.size() > length)? length: static_cast<std::streamsize>(_postfix.size());
85 std::memcpy(buffer, _postfix.c_str(), tmp);
86 _postfix = _postfix.substr(tmp);
87 return static_cast<int>(tmp);
88 }
89 else
90 return -1;
91 }
92
93 if (!_pIstr->good())
94 return -1;
95
96 if (_numBytes < length)
97 length = static_cast<std::streamsize>(_numBytes);
98
99 _pIstr->read(buffer, length);
100 std::streamsize bytesRead = _pIstr->gcount();
101 _numBytes -= bytesRead;
102 return static_cast<int>(bytesRead);
103
104}
105
106
107int PartialStreamBuf::writeToDevice(const char* buffer, std::streamsize length)
108{
109 if (_pOstr == 0 || length == 0) return -1;
110 if (!_initialized)
111 {
112 _initialized = true;
113 _pOstr->clear();
114 if (_pOstr->fail())
115 throw Poco::IOException("Failed to clear stream status");
116 }
117
118 if (_ignoreStart > 0)
119 {
120 if (_ignoreStart > length)
121 {
122 _ignoreStart -= length;
123 // fake return values
124 return static_cast<int>(length);
125 }
126 else
127 {
128 std::streamsize cnt = static_cast<std::streamsize>(length - _ignoreStart - _buffer.size());
129 if (cnt > 0)
130 {
131 _pOstr->write(buffer+_ignoreStart, cnt);
132 _bytesWritten += cnt;
133 }
134
135 // copy the rest into buffer
136 cnt += static_cast<std::streamsize>(_ignoreStart);
137 _ignoreStart = 0;
138 poco_assert (cnt < length);
139 _bufferOffset = static_cast<Poco::UInt32>(length - cnt);
140 std::memcpy(_buffer.begin(), buffer + cnt, _bufferOffset);
141
142 return static_cast<int>(length);
143 }
144 }
145 if (_buffer.size() > 0)
146 {
147 // always treat each write as the potential last one
148 // thus first fill the buffer with the last n bytes of the msg
149
150 // how much of the already cached data do we need to write?
151 Poco::Int32 cache = static_cast<Poco::Int32>(_bufferOffset +
152 static_cast<Poco::Int32>(length) - static_cast<Poco::Int32>(_buffer.size()));
153 if (cache > 0)
154 {
155 if (cache > _bufferOffset)
156 cache = _bufferOffset;
157 _pOstr->write(_buffer.begin(), cache);
158 _bytesWritten += cache;
159 _bufferOffset -= cache;
160 if (_bufferOffset > 0)
161 std::memmove(_buffer.begin(), _buffer.begin()+cache, _bufferOffset);
162 }
163
164 // now fill up _buffer with the last bytes from buffer
165 Poco::Int32 pos = static_cast<Poco::Int32>(length - static_cast<Poco::Int32>(_buffer.size()) + _bufferOffset);
166 if (pos <= 0)
167 {
168 // all of the message goes to _buffer
169 std::memcpy(_buffer.begin() + _bufferOffset, buffer, length);
170 }
171 else
172 {
173 poco_assert (_bufferOffset == 0);
174 std::memcpy(_buffer.begin(), buffer+pos, _buffer.size());
175 _bufferOffset = static_cast<Poco::UInt32>(_buffer.size());
176 // the rest is written
177 _pOstr->write(buffer, static_cast<std::streamsize>(length - _buffer.size()));
178 _bytesWritten += (length - _buffer.size());
179 }
180 }
181 else
182 {
183 _pOstr->write(buffer, length);
184 _bytesWritten += length;
185 }
186
187 if (_pOstr->good())
188 return static_cast<int>(length);
189
190 throw Poco::IOException("Failed to write to output stream");
191}
192
193
194void PartialStreamBuf::close()
195{
196 // DON'T write data from _buffer!
197}
198
199
200PartialIOS::PartialIOS(std::istream& istr, std::ios::pos_type start, std::ios::pos_type to, const std::string& pre, const std::string& post, bool initStream): _buf(istr, start, to, pre, post, initStream)
201{
202 poco_ios_init(&_buf);
203}
204
205
206PartialIOS::PartialIOS(std::ostream& ostr, std::size_t start, std::size_t to, bool initStream): _buf(ostr, start, to, initStream)
207{
208 poco_ios_init(&_buf);
209}
210
211
212PartialIOS::~PartialIOS()
213{
214}
215
216
217PartialStreamBuf* PartialIOS::rdbuf()
218{
219 return &_buf;
220}
221
222
223PartialInputStream::PartialInputStream(std::istream& istr, std::ios::pos_type start, std::ios::pos_type to, bool initStream, const std::string& pre, const std::string& post):
224 PartialIOS(istr, start, to, pre, post, initStream),
225 std::istream(&_buf)
226{
227}
228
229
230PartialInputStream::~PartialInputStream()
231{
232}
233
234
235PartialOutputStream::PartialOutputStream(std::ostream& ostr, std::size_t start, std::size_t to, bool initStream):
236 PartialIOS(ostr, start, to, initStream),
237 std::ostream(&_buf)
238{
239}
240
241
242PartialOutputStream::~PartialOutputStream()
243{
244 try
245 {
246 close();
247 }
248 catch (...)
249 {
250 poco_unexpected();
251 }
252}
253
254
255} } // namespace Poco::Zip
256