]>
Commit | Line | Data |
---|---|---|
fa736190 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2013 The Bitcoin Core developers |
78253fcb | 3 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 4 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
fa736190 CF |
5 | |
6 | #ifndef BITCOIN_STREAMS_H | |
7 | #define BITCOIN_STREAMS_H | |
8 | ||
d7d187e8 | 9 | #include "support/allocators/zeroafterfree.h" |
fa736190 CF |
10 | #include "serialize.h" |
11 | ||
12 | #include <algorithm> | |
13 | #include <assert.h> | |
14 | #include <ios> | |
15 | #include <limits> | |
16 | #include <map> | |
17 | #include <set> | |
18 | #include <stdint.h> | |
1630219d | 19 | #include <stdio.h> |
fa736190 CF |
20 | #include <string> |
21 | #include <string.h> | |
22 | #include <utility> | |
23 | #include <vector> | |
24 | ||
b7e75b17 JG |
25 | template<typename Stream> |
26 | class OverrideStream | |
27 | { | |
28 | Stream* stream; | |
29 | ||
30 | const int nType; | |
31 | const int nVersion; | |
32 | ||
33 | public: | |
34 | OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {} | |
35 | ||
36 | template<typename T> | |
37 | OverrideStream<Stream>& operator<<(const T& obj) | |
38 | { | |
39 | // Serialize to this stream | |
40 | ::Serialize(*this, obj); | |
41 | return (*this); | |
42 | } | |
43 | ||
44 | template<typename T> | |
45 | OverrideStream<Stream>& operator>>(T&& obj) | |
46 | { | |
47 | // Unserialize from this stream | |
48 | ::Unserialize(*this, obj); | |
49 | return (*this); | |
50 | } | |
51 | ||
52 | void write(const char* pch, size_t nSize) | |
53 | { | |
54 | stream->write(pch, nSize); | |
55 | } | |
56 | ||
57 | void read(char* pch, size_t nSize) | |
58 | { | |
59 | stream->read(pch, nSize); | |
60 | } | |
61 | ||
62 | int GetVersion() const { return nVersion; } | |
63 | int GetType() const { return nType; } | |
64 | }; | |
65 | ||
66 | template<typename S> | |
67 | OverrideStream<S> WithVersion(S* s, int nVersion) | |
68 | { | |
69 | return OverrideStream<S>(s, s->GetType(), nVersion); | |
70 | } | |
71 | ||
fa736190 CF |
72 | /** Double ended buffer combining vector and stream-like interfaces. |
73 | * | |
74 | * >> and << read and write unformatted data using the above serialization templates. | |
75 | * Fills with data in linear time; some stringstream implementations take N^2 time. | |
76 | */ | |
6bffc46a JG |
77 | template<typename SerializeType> |
78 | class CBaseDataStream | |
fa736190 CF |
79 | { |
80 | protected: | |
6bffc46a | 81 | typedef SerializeType vector_type; |
fa736190 CF |
82 | vector_type vch; |
83 | unsigned int nReadPos; | |
7f4acac4 | 84 | |
fa736190 CF |
85 | int nType; |
86 | int nVersion; | |
7f4acac4 | 87 | public: |
fa736190 | 88 | |
6bffc46a JG |
89 | typedef typename vector_type::allocator_type allocator_type; |
90 | typedef typename vector_type::size_type size_type; | |
91 | typedef typename vector_type::difference_type difference_type; | |
92 | typedef typename vector_type::reference reference; | |
93 | typedef typename vector_type::const_reference const_reference; | |
94 | typedef typename vector_type::value_type value_type; | |
95 | typedef typename vector_type::iterator iterator; | |
96 | typedef typename vector_type::const_iterator const_iterator; | |
97 | typedef typename vector_type::reverse_iterator reverse_iterator; | |
98 | ||
99 | explicit CBaseDataStream(int nTypeIn, int nVersionIn) | |
fa736190 CF |
100 | { |
101 | Init(nTypeIn, nVersionIn); | |
102 | } | |
103 | ||
6bffc46a | 104 | CBaseDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) |
fa736190 CF |
105 | { |
106 | Init(nTypeIn, nVersionIn); | |
107 | } | |
108 | ||
109 | #if !defined(_MSC_VER) || _MSC_VER >= 1300 | |
6bffc46a | 110 | CBaseDataStream(const char* pbegin, const char* pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) |
fa736190 CF |
111 | { |
112 | Init(nTypeIn, nVersionIn); | |
113 | } | |
114 | #endif | |
115 | ||
6bffc46a | 116 | CBaseDataStream(const vector_type& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) |
fa736190 CF |
117 | { |
118 | Init(nTypeIn, nVersionIn); | |
119 | } | |
120 | ||
6bffc46a | 121 | CBaseDataStream(const std::vector<char>& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) |
fa736190 CF |
122 | { |
123 | Init(nTypeIn, nVersionIn); | |
124 | } | |
125 | ||
6bffc46a | 126 | CBaseDataStream(const std::vector<unsigned char>& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) |
fa736190 CF |
127 | { |
128 | Init(nTypeIn, nVersionIn); | |
129 | } | |
130 | ||
d1c9ef86 CF |
131 | template <typename... Args> |
132 | CBaseDataStream(int nTypeIn, int nVersionIn, Args&&... args) | |
133 | { | |
134 | Init(nTypeIn, nVersionIn); | |
242f1421 | 135 | ::SerializeMany(*this, std::forward<Args>(args)...); |
d1c9ef86 CF |
136 | } |
137 | ||
fa736190 CF |
138 | void Init(int nTypeIn, int nVersionIn) |
139 | { | |
140 | nReadPos = 0; | |
141 | nType = nTypeIn; | |
142 | nVersion = nVersionIn; | |
143 | } | |
144 | ||
6bffc46a | 145 | CBaseDataStream& operator+=(const CBaseDataStream& b) |
fa736190 CF |
146 | { |
147 | vch.insert(vch.end(), b.begin(), b.end()); | |
148 | return *this; | |
149 | } | |
150 | ||
6bffc46a | 151 | friend CBaseDataStream operator+(const CBaseDataStream& a, const CBaseDataStream& b) |
fa736190 | 152 | { |
6bffc46a | 153 | CBaseDataStream ret = a; |
fa736190 CF |
154 | ret += b; |
155 | return (ret); | |
156 | } | |
157 | ||
158 | std::string str() const | |
159 | { | |
160 | return (std::string(begin(), end())); | |
161 | } | |
162 | ||
163 | ||
164 | // | |
165 | // Vector subset | |
166 | // | |
167 | const_iterator begin() const { return vch.begin() + nReadPos; } | |
168 | iterator begin() { return vch.begin() + nReadPos; } | |
169 | const_iterator end() const { return vch.end(); } | |
170 | iterator end() { return vch.end(); } | |
171 | size_type size() const { return vch.size() - nReadPos; } | |
172 | bool empty() const { return vch.size() == nReadPos; } | |
173 | void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } | |
174 | void reserve(size_type n) { vch.reserve(n + nReadPos); } | |
175 | const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } | |
176 | reference operator[](size_type pos) { return vch[pos + nReadPos]; } | |
177 | void clear() { vch.clear(); nReadPos = 0; } | |
178 | iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } | |
179 | void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } | |
180 | ||
181 | void insert(iterator it, std::vector<char>::const_iterator first, std::vector<char>::const_iterator last) | |
182 | { | |
1878f3a7 PW |
183 | if (last == first) return; |
184 | assert(last - first > 0); | |
fa736190 CF |
185 | if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) |
186 | { | |
187 | // special case for inserting at the front when there's room | |
188 | nReadPos -= (last - first); | |
189 | memcpy(&vch[nReadPos], &first[0], last - first); | |
190 | } | |
191 | else | |
192 | vch.insert(it, first, last); | |
193 | } | |
194 | ||
195 | #if !defined(_MSC_VER) || _MSC_VER >= 1300 | |
196 | void insert(iterator it, const char* first, const char* last) | |
197 | { | |
1878f3a7 PW |
198 | if (last == first) return; |
199 | assert(last - first > 0); | |
fa736190 CF |
200 | if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) |
201 | { | |
202 | // special case for inserting at the front when there's room | |
203 | nReadPos -= (last - first); | |
204 | memcpy(&vch[nReadPos], &first[0], last - first); | |
205 | } | |
206 | else | |
207 | vch.insert(it, first, last); | |
208 | } | |
209 | #endif | |
210 | ||
211 | iterator erase(iterator it) | |
212 | { | |
213 | if (it == vch.begin() + nReadPos) | |
214 | { | |
215 | // special case for erasing from the front | |
216 | if (++nReadPos >= vch.size()) | |
217 | { | |
218 | // whenever we reach the end, we take the opportunity to clear the buffer | |
219 | nReadPos = 0; | |
220 | return vch.erase(vch.begin(), vch.end()); | |
221 | } | |
222 | return vch.begin() + nReadPos; | |
223 | } | |
224 | else | |
225 | return vch.erase(it); | |
226 | } | |
227 | ||
228 | iterator erase(iterator first, iterator last) | |
229 | { | |
230 | if (first == vch.begin() + nReadPos) | |
231 | { | |
232 | // special case for erasing from the front | |
233 | if (last == vch.end()) | |
234 | { | |
235 | nReadPos = 0; | |
236 | return vch.erase(vch.begin(), vch.end()); | |
237 | } | |
238 | else | |
239 | { | |
240 | nReadPos = (last - vch.begin()); | |
241 | return last; | |
242 | } | |
243 | } | |
244 | else | |
245 | return vch.erase(first, last); | |
246 | } | |
247 | ||
248 | inline void Compact() | |
249 | { | |
250 | vch.erase(vch.begin(), vch.begin() + nReadPos); | |
251 | nReadPos = 0; | |
252 | } | |
253 | ||
254 | bool Rewind(size_type n) | |
255 | { | |
256 | // Rewind by n characters if the buffer hasn't been compacted yet | |
257 | if (n > nReadPos) | |
258 | return false; | |
259 | nReadPos -= n; | |
260 | return true; | |
261 | } | |
262 | ||
263 | ||
264 | // | |
265 | // Stream subset | |
266 | // | |
267 | bool eof() const { return size() == 0; } | |
6bffc46a | 268 | CBaseDataStream* rdbuf() { return this; } |
fa736190 CF |
269 | int in_avail() { return size(); } |
270 | ||
271 | void SetType(int n) { nType = n; } | |
7f4acac4 | 272 | int GetType() const { return nType; } |
fa736190 | 273 | void SetVersion(int n) { nVersion = n; } |
7f4acac4 | 274 | int GetVersion() const { return nVersion; } |
fa736190 | 275 | |
1315591c | 276 | void read(char* pch, size_t nSize) |
fa736190 | 277 | { |
1878f3a7 PW |
278 | if (nSize == 0) return; |
279 | ||
54a872f0 S |
280 | if (pch == nullptr) { |
281 | throw std::ios_base::failure("CBaseDataStream::read(): cannot read from null pointer"); | |
282 | } | |
283 | ||
fa736190 CF |
284 | // Read from the beginning of the buffer |
285 | unsigned int nReadPosNext = nReadPos + nSize; | |
286 | if (nReadPosNext >= vch.size()) | |
287 | { | |
288 | if (nReadPosNext > vch.size()) | |
289 | { | |
6bffc46a | 290 | throw std::ios_base::failure("CBaseDataStream::read(): end of data"); |
fa736190 CF |
291 | } |
292 | memcpy(pch, &vch[nReadPos], nSize); | |
293 | nReadPos = 0; | |
294 | vch.clear(); | |
1315591c | 295 | return; |
fa736190 CF |
296 | } |
297 | memcpy(pch, &vch[nReadPos], nSize); | |
298 | nReadPos = nReadPosNext; | |
fa736190 CF |
299 | } |
300 | ||
1315591c | 301 | void ignore(int nSize) |
fa736190 CF |
302 | { |
303 | // Ignore from the beginning of the buffer | |
e9d221e7 PS |
304 | if (nSize < 0) { |
305 | throw std::ios_base::failure("CDataStream::ignore(): nSize negative"); | |
306 | } | |
fa736190 CF |
307 | unsigned int nReadPosNext = nReadPos + nSize; |
308 | if (nReadPosNext >= vch.size()) | |
309 | { | |
310 | if (nReadPosNext > vch.size()) | |
6bffc46a | 311 | throw std::ios_base::failure("CBaseDataStream::ignore(): end of data"); |
fa736190 CF |
312 | nReadPos = 0; |
313 | vch.clear(); | |
1315591c | 314 | return; |
fa736190 CF |
315 | } |
316 | nReadPos = nReadPosNext; | |
fa736190 CF |
317 | } |
318 | ||
1315591c | 319 | void write(const char* pch, size_t nSize) |
fa736190 CF |
320 | { |
321 | // Write to the end of the buffer | |
322 | vch.insert(vch.end(), pch, pch + nSize); | |
fa736190 CF |
323 | } |
324 | ||
325 | template<typename Stream> | |
242f1421 | 326 | void Serialize(Stream& s) const |
fa736190 CF |
327 | { |
328 | // Special case: stream << stream concatenates like stream += stream | |
329 | if (!vch.empty()) | |
330 | s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); | |
331 | } | |
332 | ||
fa736190 | 333 | template<typename T> |
6bffc46a | 334 | CBaseDataStream& operator<<(const T& obj) |
fa736190 CF |
335 | { |
336 | // Serialize to this stream | |
242f1421 | 337 | ::Serialize(*this, obj); |
fa736190 CF |
338 | return (*this); |
339 | } | |
340 | ||
341 | template<typename T> | |
6bffc46a | 342 | CBaseDataStream& operator>>(T& obj) |
fa736190 CF |
343 | { |
344 | // Unserialize from this stream | |
242f1421 | 345 | ::Unserialize(*this, obj); |
fa736190 CF |
346 | return (*this); |
347 | } | |
348 | ||
f0e90192 PJ |
349 | void GetAndClear(CSerializeData &d) { |
350 | d.insert(d.end(), begin(), end()); | |
fa736190 CF |
351 | clear(); |
352 | } | |
353 | }; | |
354 | ||
6bffc46a JG |
355 | class CDataStream : public CBaseDataStream<CSerializeData> |
356 | { | |
357 | public: | |
358 | explicit CDataStream(int nTypeIn, int nVersionIn) : CBaseDataStream(nTypeIn, nVersionIn) { } | |
359 | ||
360 | CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn, int nVersionIn) : | |
361 | CBaseDataStream(pbegin, pend, nTypeIn, nVersionIn) { } | |
362 | ||
363 | #if !defined(_MSC_VER) || _MSC_VER >= 1300 | |
364 | CDataStream(const char* pbegin, const char* pend, int nTypeIn, int nVersionIn) : | |
365 | CBaseDataStream(pbegin, pend, nTypeIn, nVersionIn) { } | |
366 | #endif | |
367 | ||
368 | CDataStream(const vector_type& vchIn, int nTypeIn, int nVersionIn) : | |
369 | CBaseDataStream(vchIn, nTypeIn, nVersionIn) { } | |
370 | ||
371 | CDataStream(const std::vector<char>& vchIn, int nTypeIn, int nVersionIn) : | |
372 | CBaseDataStream(vchIn, nTypeIn, nVersionIn) { } | |
373 | ||
374 | CDataStream(const std::vector<unsigned char>& vchIn, int nTypeIn, int nVersionIn) : | |
375 | CBaseDataStream(vchIn, nTypeIn, nVersionIn) { } | |
376 | ||
d1c9ef86 CF |
377 | template <typename... Args> |
378 | CDataStream(int nTypeIn, int nVersionIn, Args&&... args) : | |
379 | CBaseDataStream(nTypeIn, nVersionIn, args...) { } | |
380 | ||
6bffc46a JG |
381 | }; |
382 | ||
fa736190 CF |
383 | |
384 | ||
385 | ||
386 | ||
387 | ||
388 | ||
389 | ||
390 | ||
391 | ||
392 | /** Non-refcounted RAII wrapper for FILE* | |
393 | * | |
394 | * Will automatically close the file when it goes out of scope if not null. | |
395 | * If you're returning the file pointer, return file.release(). | |
396 | * If you need to close the file early, use file.fclose() instead of fclose(file). | |
397 | */ | |
398 | class CAutoFile | |
399 | { | |
400 | private: | |
401 | // Disallow copies | |
402 | CAutoFile(const CAutoFile&); | |
403 | CAutoFile& operator=(const CAutoFile&); | |
404 | ||
7f4acac4 PW |
405 | const int nType; |
406 | const int nVersion; | |
407 | ||
fa736190 CF |
408 | FILE* file; |
409 | ||
410 | public: | |
7f4acac4 | 411 | CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) |
fa736190 CF |
412 | { |
413 | file = filenew; | |
fa736190 CF |
414 | } |
415 | ||
416 | ~CAutoFile() | |
417 | { | |
418 | fclose(); | |
419 | } | |
420 | ||
421 | void fclose() | |
422 | { | |
423 | if (file) { | |
424 | ::fclose(file); | |
425 | file = NULL; | |
426 | } | |
427 | } | |
428 | ||
429 | /** Get wrapped FILE* with transfer of ownership. | |
430 | * @note This will invalidate the CAutoFile object, and makes it the responsibility of the caller | |
431 | * of this function to clean up the returned FILE*. | |
432 | */ | |
433 | FILE* release() { FILE* ret = file; file = NULL; return ret; } | |
434 | ||
435 | /** Get wrapped FILE* without transfer of ownership. | |
436 | * @note Ownership of the FILE* will remain with this class. Use this only if the scope of the | |
437 | * CAutoFile outlives use of the passed pointer. | |
438 | */ | |
439 | FILE* Get() const { return file; } | |
440 | ||
441 | /** Return true if the wrapped FILE* is NULL, false otherwise. | |
442 | */ | |
443 | bool IsNull() const { return (file == NULL); } | |
444 | ||
445 | // | |
446 | // Stream subset | |
447 | // | |
7f4acac4 PW |
448 | int GetType() const { return nType; } |
449 | int GetVersion() const { return nVersion; } | |
fa736190 | 450 | |
1315591c | 451 | void read(char* pch, size_t nSize) |
fa736190 CF |
452 | { |
453 | if (!file) | |
5262fde0 | 454 | throw std::ios_base::failure("CAutoFile::read: file handle is NULL"); |
fa736190 | 455 | if (fread(pch, 1, nSize, file) != nSize) |
5262fde0 | 456 | throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed"); |
fa736190 CF |
457 | } |
458 | ||
1315591c | 459 | void ignore(size_t nSize) |
f588c5ed PW |
460 | { |
461 | if (!file) | |
462 | throw std::ios_base::failure("CAutoFile::ignore: file handle is NULL"); | |
463 | unsigned char data[4096]; | |
464 | while (nSize > 0) { | |
465 | size_t nNow = std::min<size_t>(nSize, sizeof(data)); | |
466 | if (fread(data, 1, nNow, file) != nNow) | |
467 | throw std::ios_base::failure(feof(file) ? "CAutoFile::ignore: end of file" : "CAutoFile::read: fread failed"); | |
468 | nSize -= nNow; | |
469 | } | |
f588c5ed PW |
470 | } |
471 | ||
1315591c | 472 | void write(const char* pch, size_t nSize) |
fa736190 CF |
473 | { |
474 | if (!file) | |
5262fde0 | 475 | throw std::ios_base::failure("CAutoFile::write: file handle is NULL"); |
fa736190 | 476 | if (fwrite(pch, 1, nSize, file) != nSize) |
5262fde0 | 477 | throw std::ios_base::failure("CAutoFile::write: write failed"); |
fa736190 | 478 | } |
fa736190 CF |
479 | |
480 | template<typename T> | |
481 | CAutoFile& operator<<(const T& obj) | |
482 | { | |
483 | // Serialize to this stream | |
484 | if (!file) | |
5262fde0 | 485 | throw std::ios_base::failure("CAutoFile::operator<<: file handle is NULL"); |
242f1421 | 486 | ::Serialize(*this, obj); |
fa736190 CF |
487 | return (*this); |
488 | } | |
489 | ||
490 | template<typename T> | |
491 | CAutoFile& operator>>(T& obj) | |
492 | { | |
493 | // Unserialize from this stream | |
494 | if (!file) | |
5262fde0 | 495 | throw std::ios_base::failure("CAutoFile::operator>>: file handle is NULL"); |
242f1421 | 496 | ::Unserialize(*this, obj); |
fa736190 CF |
497 | return (*this); |
498 | } | |
499 | }; | |
500 | ||
501 | /** Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to | |
502 | * deserialize from. It guarantees the ability to rewind a given number of bytes. | |
503 | * | |
504 | * Will automatically close the file when it goes out of scope if not null. | |
505 | * If you need to close the file early, use file.fclose() instead of fclose(file). | |
506 | */ | |
507 | class CBufferedFile | |
508 | { | |
509 | private: | |
510 | // Disallow copies | |
511 | CBufferedFile(const CBufferedFile&); | |
512 | CBufferedFile& operator=(const CBufferedFile&); | |
513 | ||
7f4acac4 PW |
514 | const int nType; |
515 | const int nVersion; | |
fa736190 CF |
516 | |
517 | FILE *src; // source file | |
518 | uint64_t nSrcPos; // how many bytes have been read from source | |
519 | uint64_t nReadPos; // how many bytes have been read from this | |
520 | uint64_t nReadLimit; // up to which position we're allowed to read | |
521 | uint64_t nRewind; // how many bytes we guarantee to rewind | |
522 | std::vector<char> vchBuf; // the buffer | |
523 | ||
524 | protected: | |
525 | // read data from the source to fill the buffer | |
526 | bool Fill() { | |
527 | unsigned int pos = nSrcPos % vchBuf.size(); | |
528 | unsigned int readNow = vchBuf.size() - pos; | |
529 | unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; | |
530 | if (nAvail < readNow) | |
531 | readNow = nAvail; | |
532 | if (readNow == 0) | |
533 | return false; | |
534 | size_t read = fread((void*)&vchBuf[pos], 1, readNow, src); | |
535 | if (read == 0) { | |
5262fde0 | 536 | throw std::ios_base::failure(feof(src) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed"); |
fa736190 CF |
537 | } else { |
538 | nSrcPos += read; | |
539 | return true; | |
540 | } | |
541 | } | |
542 | ||
543 | public: | |
544 | CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) : | |
7f4acac4 | 545 | nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0) |
fa736190 CF |
546 | { |
547 | src = fileIn; | |
fa736190 CF |
548 | } |
549 | ||
550 | ~CBufferedFile() | |
551 | { | |
552 | fclose(); | |
553 | } | |
554 | ||
242f1421 PW |
555 | int GetVersion() const { return nVersion; } |
556 | int GetType() const { return nType; } | |
557 | ||
fa736190 CF |
558 | void fclose() |
559 | { | |
560 | if (src) { | |
561 | ::fclose(src); | |
562 | src = NULL; | |
563 | } | |
564 | } | |
565 | ||
566 | // check whether we're at the end of the source file | |
567 | bool eof() const { | |
568 | return nReadPos == nSrcPos && feof(src); | |
569 | } | |
570 | ||
571 | // read a number of bytes | |
1315591c | 572 | void read(char *pch, size_t nSize) { |
54a872f0 S |
573 | if (nSize == 0) return; |
574 | ||
575 | if (pch == nullptr) { | |
576 | throw std::ios_base::failure("CBufferedFile::read(): cannot read from null pointer"); | |
577 | } | |
578 | ||
fa736190 CF |
579 | if (nSize + nReadPos > nReadLimit) |
580 | throw std::ios_base::failure("Read attempted past buffer limit"); | |
581 | if (nSize + nRewind > vchBuf.size()) | |
582 | throw std::ios_base::failure("Read larger than buffer size"); | |
583 | while (nSize > 0) { | |
584 | if (nReadPos == nSrcPos) | |
585 | Fill(); | |
586 | unsigned int pos = nReadPos % vchBuf.size(); | |
587 | size_t nNow = nSize; | |
588 | if (nNow + pos > vchBuf.size()) | |
589 | nNow = vchBuf.size() - pos; | |
590 | if (nNow + nReadPos > nSrcPos) | |
591 | nNow = nSrcPos - nReadPos; | |
592 | memcpy(pch, &vchBuf[pos], nNow); | |
593 | nReadPos += nNow; | |
594 | pch += nNow; | |
595 | nSize -= nNow; | |
596 | } | |
fa736190 CF |
597 | } |
598 | ||
599 | // return the current reading position | |
600 | uint64_t GetPos() { | |
601 | return nReadPos; | |
602 | } | |
603 | ||
604 | // rewind to a given reading position | |
605 | bool SetPos(uint64_t nPos) { | |
606 | nReadPos = nPos; | |
607 | if (nReadPos + nRewind < nSrcPos) { | |
608 | nReadPos = nSrcPos - nRewind; | |
609 | return false; | |
610 | } else if (nReadPos > nSrcPos) { | |
611 | nReadPos = nSrcPos; | |
612 | return false; | |
613 | } else { | |
614 | return true; | |
615 | } | |
616 | } | |
617 | ||
618 | bool Seek(uint64_t nPos) { | |
619 | long nLongPos = nPos; | |
620 | if (nPos != (uint64_t)nLongPos) | |
621 | return false; | |
622 | if (fseek(src, nLongPos, SEEK_SET)) | |
623 | return false; | |
624 | nLongPos = ftell(src); | |
625 | nSrcPos = nLongPos; | |
626 | nReadPos = nLongPos; | |
627 | return true; | |
628 | } | |
629 | ||
630 | // prevent reading beyond a certain position | |
631 | // no argument removes the limit | |
632 | bool SetLimit(uint64_t nPos = (uint64_t)(-1)) { | |
633 | if (nPos < nReadPos) | |
634 | return false; | |
635 | nReadLimit = nPos; | |
636 | return true; | |
637 | } | |
638 | ||
639 | template<typename T> | |
640 | CBufferedFile& operator>>(T& obj) { | |
641 | // Unserialize from this stream | |
242f1421 | 642 | ::Unserialize(*this, obj); |
fa736190 CF |
643 | return (*this); |
644 | } | |
645 | ||
646 | // search for a given byte in the stream, and remain positioned on it | |
647 | void FindByte(char ch) { | |
648 | while (true) { | |
649 | if (nReadPos == nSrcPos) | |
650 | Fill(); | |
651 | if (vchBuf[nReadPos % vchBuf.size()] == ch) | |
652 | break; | |
653 | nReadPos++; | |
654 | } | |
655 | } | |
656 | }; | |
657 | ||
658 | #endif // BITCOIN_STREAMS_H |