Wip: more compact serialize.h
[novacoin.git] / src / serialize.cpp
1 #include "serialize.h"
2 #include "hash.h"
3
4 template<typename Stream>
5 void WriteCompactSize(Stream& os, uint64_t nSize)
6 {
7     if (nSize < 0xfd)
8     {
9         auto chSize = (uint8_t)nSize;
10         WRITEDATA(os, chSize);
11     }
12     else if (nSize <= std::numeric_limits<uint16_t>::max())
13     {
14         uint8_t chSize = 0xfd;
15         auto xSize = (uint16_t)nSize;
16         WRITEDATA(os, chSize);
17         WRITEDATA(os, xSize);
18     }
19     else if (nSize <= std::numeric_limits<uint32_t>::max())
20     {
21         uint8_t chSize = 0xfe;
22         auto xSize = (uint32_t)nSize;
23         WRITEDATA(os, chSize);
24         WRITEDATA(os, xSize);
25     }
26     else
27     {
28         uint8_t chSize = 0xff;
29         auto xSize = nSize;
30         WRITEDATA(os, chSize);
31         WRITEDATA(os, xSize);
32     }
33     return;
34 }
35
36
37 template<typename Stream>
38 uint64_t ReadCompactSize(Stream& is)
39 {
40     uint8_t chSize;
41     READDATA(is, chSize);
42     uint64_t nSizeRet = 0;
43     if (chSize < 0xfd)
44     {
45         nSizeRet = chSize;
46     }
47     else if (chSize == 0xfd)
48     {
49         uint16_t xSize;
50         READDATA(is, xSize);
51         nSizeRet = xSize;
52     }
53     else if (chSize == 0xfe)
54     {
55         uint32_t xSize;
56         READDATA(is, xSize);
57         nSizeRet = xSize;
58     }
59     else
60     {
61         uint64_t xSize;
62         READDATA(is, xSize);
63         nSizeRet = xSize;
64     }
65     if (nSizeRet > (uint64_t)MAX_SIZE)
66         throw std::ios_base::failure("ReadCompactSize() : size too large");
67     return nSizeRet;
68 }
69
70
71
72 // Template instantiation
73
74 template void WriteCompactSize<CAutoFile>(CAutoFile&, uint64_t);
75 template void WriteCompactSize<CDataStream>(CDataStream&, uint64_t);
76 template void WriteCompactSize<CHashWriter>(CHashWriter&, uint64_t);
77
78 template uint64_t ReadCompactSize<CAutoFile>(CAutoFile&);
79 template uint64_t ReadCompactSize<CDataStream>(CDataStream&);
80
81 //
82 //CDataStream
83 //
84
85
86 bool CDataStream::Rewind(size_type n)
87 {
88     // Rewind by n characters if the buffer hasn't been compacted yet
89     if (n > nReadPos)
90         return false;
91     nReadPos -= (unsigned int)n;
92     return true;
93 }
94
95 void CDataStream::setstate(short bits, const char* psz)
96 {
97     state |= bits;
98     if (state & exceptmask)
99         throw std::ios_base::failure(psz);
100 }
101
102 CDataStream& CDataStream::read(char* pch, int nSize)
103 {
104     // Read from the beginning of the buffer
105     assert(nSize >= 0);
106     unsigned int nReadPosNext = nReadPos + nSize;
107     if (nReadPosNext >= vch.size())
108     {
109         if (nReadPosNext > vch.size())
110         {
111             setstate(std::ios::failbit, "CDataStream::read() : end of data");
112             memset(pch, 0, nSize);
113             nSize = (int)(vch.size() - nReadPos);
114         }
115         memcpy(pch, &vch[nReadPos], nSize);
116         nReadPos = 0;
117         vch.clear();
118         return (*this);
119     }
120     memcpy(pch, &vch[nReadPos], nSize);
121     nReadPos = nReadPosNext;
122     return (*this);
123 }
124
125 CDataStream& CDataStream::ignore(int nSize)
126 {
127     // Ignore from the beginning of the buffer
128     assert(nSize >= 0);
129     unsigned int nReadPosNext = nReadPos + nSize;
130     if (nReadPosNext >= vch.size())
131     {
132         if (nReadPosNext > vch.size())
133             setstate(std::ios::failbit, "CDataStream::ignore() : end of data");
134         nReadPos = 0;
135         vch.clear();
136         return (*this);
137     }
138     nReadPos = nReadPosNext;
139     return (*this);
140 }
141
142 CDataStream& CDataStream::write(const char* pch, int nSize)
143 {
144     // Write to the end of the buffer
145     assert(nSize >= 0);
146     vch.insert(vch.end(), pch, pch + nSize);
147     return (*this);
148 }
149
150 void CDataStream::GetAndClear(CSerializeData &data) {
151     vch.swap(data);
152     CSerializeData().swap(vch);
153 }
154
155 //
156 //CAutoFile
157 //
158
159 CAutoFile::CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn)
160 {
161     file = filenew;
162     nType = nTypeIn;
163     nVersion = nVersionIn;
164     state = 0;
165     exceptmask = std::ios::badbit | std::ios::failbit;
166 }
167
168 CAutoFile::~CAutoFile()
169 {
170     fclose();
171 }
172
173 void CAutoFile::fclose()
174 {
175     if (file != NULL && file != stdin && file != stdout && file != stderr)
176         ::fclose(file);
177     file = NULL;
178 }
179
180 void CAutoFile::setstate(short bits, const char* psz)
181 {
182     state |= bits;
183     if (state & exceptmask)
184         throw std::ios_base::failure(psz);
185 }
186
187 CAutoFile& CAutoFile::read(char* pch, size_t nSize)
188 {
189     if (!file)
190         throw std::ios_base::failure("CAutoFile::read : file handle is NULL");
191     if (fread(pch, 1, nSize, file) != nSize)
192         setstate(std::ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed");
193     return (*this);
194 }
195
196 CAutoFile& CAutoFile::write(const char* pch, size_t nSize)
197 {
198     if (!file)
199         throw std::ios_base::failure("CAutoFile::write : file handle is NULL");
200     if (fwrite(pch, 1, nSize, file) != nSize)
201         setstate(std::ios::failbit, "CAutoFile::write : write failed");
202     return (*this);
203 }
204
205 //
206 //CBufferedFile
207 //
208
209 void CBufferedFile::setstate(short bits, const char *psz)
210 {
211     state |= bits;
212     if (state & exceptmask)
213         throw std::ios_base::failure(psz);
214 }
215
216 bool CBufferedFile::Fill()
217 {
218     auto pos = (uint32_t)(nSrcPos % vchBuf.size());
219     auto readNow = (uint32_t)(vchBuf.size() - pos);
220     auto nAvail = (uint32_t)(vchBuf.size() - (nSrcPos - nReadPos) - nRewind);
221     if (nAvail < readNow)
222         readNow = nAvail;
223     if (readNow == 0)
224         return false;
225     size_t read = fread((void*)&vchBuf[pos], 1, readNow, src);
226     if (read == 0) {
227         setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed");
228         return false;
229     } else {
230         nSrcPos += read;
231         return true;
232     }
233 }
234
235 CBufferedFile::CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
236     src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, 0),
237     state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { }
238
239 bool CBufferedFile::good() const
240 {
241     return state == 0;
242 }
243
244 bool CBufferedFile::eof() const
245 {
246     return nReadPos == nSrcPos && feof(src);
247 }
248
249 CBufferedFile& CBufferedFile::read(char *pch, size_t nSize)
250 {
251     if (nSize + nReadPos > nReadLimit)
252         throw std::ios_base::failure("Read attempted past buffer limit");
253     if (nSize + nRewind > vchBuf.size())
254         throw std::ios_base::failure("Read larger than buffer size");
255     while (nSize > 0) {
256         if (nReadPos == nSrcPos)
257             Fill();
258         auto pos = (uint32_t)(nReadPos % vchBuf.size());
259         auto nNow = nSize;
260         if (nNow + pos > vchBuf.size())
261             nNow = vchBuf.size() - pos;
262         if (nNow + nReadPos > nSrcPos)
263             nNow = (size_t)(nSrcPos - nReadPos);
264         memcpy(pch, &vchBuf[pos], nNow);
265         nReadPos += nNow;
266         pch += nNow;
267         nSize -= nNow;
268     }
269     return (*this);
270 }
271
272 uint64_t CBufferedFile::GetPos()
273 {
274     return nReadPos;
275 }
276
277 bool CBufferedFile::SetPos(uint64_t nPos)
278 {
279     nReadPos = nPos;
280     if (nReadPos + nRewind < nSrcPos) {
281         nReadPos = nSrcPos - nRewind;
282         return false;
283     } else if (nReadPos > nSrcPos) {
284         nReadPos = nSrcPos;
285         return false;
286     } else {
287         return true;
288     }
289 }
290
291 bool CBufferedFile::Seek(uint64_t nPos)
292 {
293     long nLongPos = (long)nPos;
294     if (nPos != (uint64_t)nLongPos)
295         return false;
296     if (fseek(src, nLongPos, SEEK_SET))
297         return false;
298     nLongPos = ftell(src);
299     nSrcPos = nLongPos;
300     nReadPos = nLongPos;
301     state = 0;
302     return true;
303 }
304
305 bool CBufferedFile::SetLimit(uint64_t nPos)
306 {
307     if (nPos < nReadPos)
308         return false;
309     nReadLimit = nPos;
310     return true;
311 }
312
313 void CBufferedFile::FindByte(char ch)
314 {
315     for ( ; ; ) {
316         if (nReadPos == nSrcPos)
317             Fill();
318         if (vchBuf[nReadPos % vchBuf.size()] == ch)
319             break;
320         nReadPos++;
321     }
322 }