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 | |
20 | namespace Poco { |
21 | namespace Zip { |
22 | |
23 | |
24 | PartialStreamBuf::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 | |
41 | PartialStreamBuf::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 | |
56 | PartialStreamBuf::~PartialStreamBuf() |
57 | { |
58 | } |
59 | |
60 | |
61 | int 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 | |
107 | int 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 | |
194 | void PartialStreamBuf::close() |
195 | { |
196 | // DON'T write data from _buffer! |
197 | } |
198 | |
199 | |
200 | PartialIOS::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 | |
206 | PartialIOS::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 | |
212 | PartialIOS::~PartialIOS() |
213 | { |
214 | } |
215 | |
216 | |
217 | PartialStreamBuf* PartialIOS::rdbuf() |
218 | { |
219 | return &_buf; |
220 | } |
221 | |
222 | |
223 | PartialInputStream::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 | |
230 | PartialInputStream::~PartialInputStream() |
231 | { |
232 | } |
233 | |
234 | |
235 | PartialOutputStream::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 | |
242 | PartialOutputStream::~PartialOutputStream() |
243 | { |
244 | try |
245 | { |
246 | close(); |
247 | } |
248 | catch (...) |
249 | { |
250 | poco_unexpected(); |
251 | } |
252 | } |
253 | |
254 | |
255 | } } // namespace Poco::Zip |
256 | |