1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2013 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 #ifndef BITCOIN_STREAMS_H
7 #define BITCOIN_STREAMS_H
9 #include "support/allocators/zeroafterfree.h"
10 #include "serialize.h"
25 /** Double ended buffer combining vector and stream-like interfaces.
27 * >> and << read and write unformatted data using the above serialization templates.
28 * Fills with data in linear time; some stringstream implementations take N^2 time.
33 typedef CSerializeData vector_type
;
35 unsigned int nReadPos
;
40 typedef vector_type::allocator_type allocator_type
;
41 typedef vector_type::size_type size_type
;
42 typedef vector_type::difference_type difference_type
;
43 typedef vector_type::reference reference
;
44 typedef vector_type::const_reference const_reference
;
45 typedef vector_type::value_type value_type
;
46 typedef vector_type::iterator iterator
;
47 typedef vector_type::const_iterator const_iterator
;
48 typedef vector_type::reverse_iterator reverse_iterator
;
50 explicit CDataStream(int nTypeIn
, int nVersionIn
)
52 Init(nTypeIn
, nVersionIn
);
55 CDataStream(const_iterator pbegin
, const_iterator pend
, int nTypeIn
, int nVersionIn
) : vch(pbegin
, pend
)
57 Init(nTypeIn
, nVersionIn
);
60 #if !defined(_MSC_VER) || _MSC_VER >= 1300
61 CDataStream(const char* pbegin
, const char* pend
, int nTypeIn
, int nVersionIn
) : vch(pbegin
, pend
)
63 Init(nTypeIn
, nVersionIn
);
67 CDataStream(const vector_type
& vchIn
, int nTypeIn
, int nVersionIn
) : vch(vchIn
.begin(), vchIn
.end())
69 Init(nTypeIn
, nVersionIn
);
72 CDataStream(const std::vector
<char>& vchIn
, int nTypeIn
, int nVersionIn
) : vch(vchIn
.begin(), vchIn
.end())
74 Init(nTypeIn
, nVersionIn
);
77 CDataStream(const std::vector
<unsigned char>& vchIn
, int nTypeIn
, int nVersionIn
) : vch(vchIn
.begin(), vchIn
.end())
79 Init(nTypeIn
, nVersionIn
);
82 void Init(int nTypeIn
, int nVersionIn
)
86 nVersion
= nVersionIn
;
89 CDataStream
& operator+=(const CDataStream
& b
)
91 vch
.insert(vch
.end(), b
.begin(), b
.end());
95 friend CDataStream
operator+(const CDataStream
& a
, const CDataStream
& b
)
102 std::string
str() const
104 return (std::string(begin(), end()));
111 const_iterator
begin() const { return vch
.begin() + nReadPos
; }
112 iterator
begin() { return vch
.begin() + nReadPos
; }
113 const_iterator
end() const { return vch
.end(); }
114 iterator
end() { return vch
.end(); }
115 size_type
size() const { return vch
.size() - nReadPos
; }
116 bool empty() const { return vch
.size() == nReadPos
; }
117 void resize(size_type n
, value_type c
=0) { vch
.resize(n
+ nReadPos
, c
); }
118 void reserve(size_type n
) { vch
.reserve(n
+ nReadPos
); }
119 const_reference
operator[](size_type pos
) const { return vch
[pos
+ nReadPos
]; }
120 reference
operator[](size_type pos
) { return vch
[pos
+ nReadPos
]; }
121 void clear() { vch
.clear(); nReadPos
= 0; }
122 iterator
insert(iterator it
, const char& x
=char()) { return vch
.insert(it
, x
); }
123 void insert(iterator it
, size_type n
, const char& x
) { vch
.insert(it
, n
, x
); }
125 void insert(iterator it
, std::vector
<char>::const_iterator first
, std::vector
<char>::const_iterator last
)
127 assert(last
- first
>= 0);
128 if (it
== vch
.begin() + nReadPos
&& (unsigned int)(last
- first
) <= nReadPos
)
130 // special case for inserting at the front when there's room
131 nReadPos
-= (last
- first
);
132 memcpy(&vch
[nReadPos
], &first
[0], last
- first
);
135 vch
.insert(it
, first
, last
);
138 #if !defined(_MSC_VER) || _MSC_VER >= 1300
139 void insert(iterator it
, const char* first
, const char* last
)
141 assert(last
- first
>= 0);
142 if (it
== vch
.begin() + nReadPos
&& (unsigned int)(last
- first
) <= nReadPos
)
144 // special case for inserting at the front when there's room
145 nReadPos
-= (last
- first
);
146 memcpy(&vch
[nReadPos
], &first
[0], last
- first
);
149 vch
.insert(it
, first
, last
);
153 iterator
erase(iterator it
)
155 if (it
== vch
.begin() + nReadPos
)
157 // special case for erasing from the front
158 if (++nReadPos
>= vch
.size())
160 // whenever we reach the end, we take the opportunity to clear the buffer
162 return vch
.erase(vch
.begin(), vch
.end());
164 return vch
.begin() + nReadPos
;
167 return vch
.erase(it
);
170 iterator
erase(iterator first
, iterator last
)
172 if (first
== vch
.begin() + nReadPos
)
174 // special case for erasing from the front
175 if (last
== vch
.end())
178 return vch
.erase(vch
.begin(), vch
.end());
182 nReadPos
= (last
- vch
.begin());
187 return vch
.erase(first
, last
);
190 inline void Compact()
192 vch
.erase(vch
.begin(), vch
.begin() + nReadPos
);
196 bool Rewind(size_type n
)
198 // Rewind by n characters if the buffer hasn't been compacted yet
209 bool eof() const { return size() == 0; }
210 CDataStream
* rdbuf() { return this; }
211 int in_avail() { return size(); }
213 void SetType(int n
) { nType
= n
; }
214 int GetType() { return nType
; }
215 void SetVersion(int n
) { nVersion
= n
; }
216 int GetVersion() { return nVersion
; }
217 void ReadVersion() { *this >> nVersion
; }
218 void WriteVersion() { *this << nVersion
; }
220 CDataStream
& read(char* pch
, size_t nSize
)
222 // Read from the beginning of the buffer
223 unsigned int nReadPosNext
= nReadPos
+ nSize
;
224 if (nReadPosNext
>= vch
.size())
226 if (nReadPosNext
> vch
.size())
228 throw std::ios_base::failure("CDataStream::read(): end of data");
230 memcpy(pch
, &vch
[nReadPos
], nSize
);
235 memcpy(pch
, &vch
[nReadPos
], nSize
);
236 nReadPos
= nReadPosNext
;
240 CDataStream
& ignore(int nSize
)
242 // Ignore from the beginning of the buffer
244 unsigned int nReadPosNext
= nReadPos
+ nSize
;
245 if (nReadPosNext
>= vch
.size())
247 if (nReadPosNext
> vch
.size())
248 throw std::ios_base::failure("CDataStream::ignore(): end of data");
253 nReadPos
= nReadPosNext
;
257 CDataStream
& write(const char* pch
, size_t nSize
)
259 // Write to the end of the buffer
260 vch
.insert(vch
.end(), pch
, pch
+ nSize
);
264 template<typename Stream
>
265 void Serialize(Stream
& s
, int nType
, int nVersion
) const
267 // Special case: stream << stream concatenates like stream += stream
269 s
.write((char*)&vch
[0], vch
.size() * sizeof(vch
[0]));
273 unsigned int GetSerializeSize(const T
& obj
)
275 // Tells the size of the object if serialized to this stream
276 return ::GetSerializeSize(obj
, nType
, nVersion
);
280 CDataStream
& operator<<(const T
& obj
)
282 // Serialize to this stream
283 ::Serialize(*this, obj
, nType
, nVersion
);
288 CDataStream
& operator>>(T
& obj
)
290 // Unserialize from this stream
291 ::Unserialize(*this, obj
, nType
, nVersion
);
295 void GetAndClear(CSerializeData
&data
) {
296 data
.insert(data
.end(), begin(), end());
310 /** Non-refcounted RAII wrapper for FILE*
312 * Will automatically close the file when it goes out of scope if not null.
313 * If you're returning the file pointer, return file.release().
314 * If you need to close the file early, use file.fclose() instead of fclose(file).
320 CAutoFile(const CAutoFile
&);
321 CAutoFile
& operator=(const CAutoFile
&);
329 CAutoFile(FILE* filenew
, int nTypeIn
, int nVersionIn
)
333 nVersion
= nVersionIn
;
349 /** Get wrapped FILE* with transfer of ownership.
350 * @note This will invalidate the CAutoFile object, and makes it the responsibility of the caller
351 * of this function to clean up the returned FILE*.
353 FILE* release() { FILE* ret
= file
; file
= NULL
; return ret
; }
355 /** Get wrapped FILE* without transfer of ownership.
356 * @note Ownership of the FILE* will remain with this class. Use this only if the scope of the
357 * CAutoFile outlives use of the passed pointer.
359 FILE* Get() const { return file
; }
361 /** Return true if the wrapped FILE* is NULL, false otherwise.
363 bool IsNull() const { return (file
== NULL
); }
368 void SetType(int n
) { nType
= n
; }
369 int GetType() { return nType
; }
370 void SetVersion(int n
) { nVersion
= n
; }
371 int GetVersion() { return nVersion
; }
372 void ReadVersion() { *this >> nVersion
; }
373 void WriteVersion() { *this << nVersion
; }
375 CAutoFile
& read(char* pch
, size_t nSize
)
378 throw std::ios_base::failure("CAutoFile::read: file handle is NULL");
379 if (fread(pch
, 1, nSize
, file
) != nSize
)
380 throw std::ios_base::failure(feof(file
) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed");
384 CAutoFile
& write(const char* pch
, size_t nSize
)
387 throw std::ios_base::failure("CAutoFile::write: file handle is NULL");
388 if (fwrite(pch
, 1, nSize
, file
) != nSize
)
389 throw std::ios_base::failure("CAutoFile::write: write failed");
394 unsigned int GetSerializeSize(const T
& obj
)
396 // Tells the size of the object if serialized to this stream
397 return ::GetSerializeSize(obj
, nType
, nVersion
);
401 CAutoFile
& operator<<(const T
& obj
)
403 // Serialize to this stream
405 throw std::ios_base::failure("CAutoFile::operator<<: file handle is NULL");
406 ::Serialize(*this, obj
, nType
, nVersion
);
411 CAutoFile
& operator>>(T
& obj
)
413 // Unserialize from this stream
415 throw std::ios_base::failure("CAutoFile::operator>>: file handle is NULL");
416 ::Unserialize(*this, obj
, nType
, nVersion
);
421 /** Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to
422 * deserialize from. It guarantees the ability to rewind a given number of bytes.
424 * Will automatically close the file when it goes out of scope if not null.
425 * If you need to close the file early, use file.fclose() instead of fclose(file).
431 CBufferedFile(const CBufferedFile
&);
432 CBufferedFile
& operator=(const CBufferedFile
&);
437 FILE *src
; // source file
438 uint64_t nSrcPos
; // how many bytes have been read from source
439 uint64_t nReadPos
; // how many bytes have been read from this
440 uint64_t nReadLimit
; // up to which position we're allowed to read
441 uint64_t nRewind
; // how many bytes we guarantee to rewind
442 std::vector
<char> vchBuf
; // the buffer
445 // read data from the source to fill the buffer
447 unsigned int pos
= nSrcPos
% vchBuf
.size();
448 unsigned int readNow
= vchBuf
.size() - pos
;
449 unsigned int nAvail
= vchBuf
.size() - (nSrcPos
- nReadPos
) - nRewind
;
450 if (nAvail
< readNow
)
454 size_t read
= fread((void*)&vchBuf
[pos
], 1, readNow
, src
);
456 throw std::ios_base::failure(feof(src
) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed");
464 CBufferedFile(FILE *fileIn
, uint64_t nBufSize
, uint64_t nRewindIn
, int nTypeIn
, int nVersionIn
) :
465 nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn
), vchBuf(nBufSize
, 0)
469 nVersion
= nVersionIn
;
485 // check whether we're at the end of the source file
487 return nReadPos
== nSrcPos
&& feof(src
);
490 // read a number of bytes
491 CBufferedFile
& read(char *pch
, size_t nSize
) {
492 if (nSize
+ nReadPos
> nReadLimit
)
493 throw std::ios_base::failure("Read attempted past buffer limit");
494 if (nSize
+ nRewind
> vchBuf
.size())
495 throw std::ios_base::failure("Read larger than buffer size");
497 if (nReadPos
== nSrcPos
)
499 unsigned int pos
= nReadPos
% vchBuf
.size();
501 if (nNow
+ pos
> vchBuf
.size())
502 nNow
= vchBuf
.size() - pos
;
503 if (nNow
+ nReadPos
> nSrcPos
)
504 nNow
= nSrcPos
- nReadPos
;
505 memcpy(pch
, &vchBuf
[pos
], nNow
);
513 // return the current reading position
518 // rewind to a given reading position
519 bool SetPos(uint64_t nPos
) {
521 if (nReadPos
+ nRewind
< nSrcPos
) {
522 nReadPos
= nSrcPos
- nRewind
;
524 } else if (nReadPos
> nSrcPos
) {
532 bool Seek(uint64_t nPos
) {
533 long nLongPos
= nPos
;
534 if (nPos
!= (uint64_t)nLongPos
)
536 if (fseek(src
, nLongPos
, SEEK_SET
))
538 nLongPos
= ftell(src
);
544 // prevent reading beyond a certain position
545 // no argument removes the limit
546 bool SetLimit(uint64_t nPos
= (uint64_t)(-1)) {
554 CBufferedFile
& operator>>(T
& obj
) {
555 // Unserialize from this stream
556 ::Unserialize(*this, obj
, nType
, nVersion
);
560 // search for a given byte in the stream, and remain positioned on it
561 void FindByte(char ch
) {
563 if (nReadPos
== nSrcPos
)
565 if (vchBuf
[nReadPos
% vchBuf
.size()] == ch
)
572 #endif // BITCOIN_STREAMS_H