]> git.cworth.org Git - apitrace/blob - common/trace_file_snappy.cpp
Build fixes for VS2013
[apitrace] / common / trace_file_snappy.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Zack Rusin
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 /*
28  * Snappy file format.
29  * -------------------
30  *
31  * Snappy at its core is just a compressoin algorithm so we're
32  * creating a new file format which uses snappy compression
33  * to hold the trace data.
34  *
35  * The file is composed of a number of chunks, they are:
36  * chunk {
37  *     uint32 - specifying the length of the compressed data
38  *     compressed data, in little endian
39  * }
40  * File can contain any number of such chunks.
41  * The default size of an uncompressed chunk is specified in
42  * SNAPPY_CHUNK_SIZE.
43  *
44  * Note:
45  * Currently the default size for a a to-be-compressed data is
46  * 1mb, meaning that the compressed data will be <= 1mb.
47  * The reason it's 1mb is because it seems
48  * to offer a pretty good compression/disk io speed ratio
49  * but that might change.
50  *
51  */
52
53
54 #include <snappy.h>
55
56 #include <iostream>
57 #include <algorithm>
58
59 #include <assert.h>
60 #include <string.h>
61
62 #include "trace_file.hpp"
63
64
65 #define SNAPPY_CHUNK_SIZE (1 * 1024 * 1024)
66
67
68
69 using namespace trace;
70
71
72 class SnappyFile : public File {
73 public:
74     SnappyFile(const std::string &filename = std::string(),
75                File::Mode mode = File::Read);
76     virtual ~SnappyFile();
77
78     virtual bool supportsOffsets() const;
79     virtual File::Offset currentOffset();
80     virtual void setCurrentOffset(const File::Offset &offset);
81 protected:
82     virtual bool rawOpen(const std::string &filename, File::Mode mode);
83     virtual bool rawWrite(const void *buffer, size_t length);
84     virtual size_t rawRead(void *buffer, size_t length);
85     virtual int rawGetc();
86     virtual void rawClose();
87     virtual void rawFlush();
88     virtual bool rawSkip(size_t length);
89     virtual int rawPercentRead();
90
91 private:
92     inline size_t usedCacheSize() const
93     {
94         assert(m_cachePtr >= m_cache);
95         return m_cachePtr - m_cache;
96     }
97     inline size_t freeCacheSize() const
98     {
99         assert(m_cacheSize >= usedCacheSize());
100         if (m_cacheSize > 0) {
101             return m_cacheSize - usedCacheSize();
102         } else {
103             return 0;
104         }
105     }
106     inline bool endOfData() const
107     {
108         return m_stream.eof() && freeCacheSize() == 0;
109     }
110     void flushWriteCache();
111     void flushReadCache(size_t skipLength = 0);
112     void createCache(size_t size);
113     void writeCompressedLength(size_t length);
114     size_t readCompressedLength();
115 private:
116     std::fstream m_stream;
117     size_t m_cacheMaxSize;
118     size_t m_cacheSize;
119     char *m_cache;
120     char *m_cachePtr;
121
122     char *m_compressedCache;
123
124     File::Offset m_currentOffset;
125     std::streampos m_endPos;
126 };
127
128 SnappyFile::SnappyFile(const std::string &filename,
129                               File::Mode mode)
130     : File(),
131       m_cacheMaxSize(SNAPPY_CHUNK_SIZE),
132       m_cacheSize(m_cacheMaxSize),
133       m_cache(new char [m_cacheMaxSize]),
134       m_cachePtr(m_cache)
135 {
136     size_t maxCompressedLength =
137         snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
138     m_compressedCache = new char[maxCompressedLength];
139 }
140
141 SnappyFile::~SnappyFile()
142 {
143     close();
144     delete [] m_compressedCache;
145     delete [] m_cache;
146 }
147
148 bool SnappyFile::rawOpen(const std::string &filename, File::Mode mode)
149 {
150     std::ios_base::openmode fmode = std::fstream::binary;
151     if (mode == File::Write) {
152         fmode |= (std::fstream::out | std::fstream::trunc);
153         createCache(SNAPPY_CHUNK_SIZE);
154     } else if (mode == File::Read) {
155         fmode |= std::fstream::in;
156     }
157
158     m_stream.open(filename.c_str(), fmode);
159
160     //read in the initial buffer if we're reading
161     if (m_stream.is_open() && mode == File::Read) {
162         m_stream.seekg(0, std::ios::end);
163         m_endPos = m_stream.tellg();
164         m_stream.seekg(0, std::ios::beg);
165
166         // read the snappy file identifier
167         unsigned char byte1, byte2;
168         m_stream >> byte1;
169         m_stream >> byte2;
170         assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);
171
172         flushReadCache();
173     } else if (m_stream.is_open() && mode == File::Write) {
174         // write the snappy file identifier
175         m_stream << SNAPPY_BYTE1;
176         m_stream << SNAPPY_BYTE2;
177     }
178     return m_stream.is_open();
179 }
180
181 bool SnappyFile::rawWrite(const void *buffer, size_t length)
182 {
183     if (freeCacheSize() > length) {
184         memcpy(m_cachePtr, buffer, length);
185         m_cachePtr += length;
186     } else if (freeCacheSize() == length) {
187         memcpy(m_cachePtr, buffer, length);
188         m_cachePtr += length;
189         flushWriteCache();
190     } else {
191         size_t sizeToWrite = length;
192
193         while (sizeToWrite >= freeCacheSize()) {
194             size_t endSize = freeCacheSize();
195             size_t offset = length - sizeToWrite;
196             memcpy(m_cachePtr, (const char*)buffer + offset, endSize);
197             sizeToWrite -= endSize;
198             m_cachePtr += endSize;
199             flushWriteCache();
200         }
201         if (sizeToWrite) {
202             size_t offset = length - sizeToWrite;
203             memcpy(m_cachePtr, (const char*)buffer + offset, sizeToWrite);
204             m_cachePtr += sizeToWrite;
205         }
206     }
207
208     return true;
209 }
210
211 size_t SnappyFile::rawRead(void *buffer, size_t length)
212 {
213     if (endOfData()) {
214         return 0;
215     }
216
217     if (freeCacheSize() >= length) {
218         memcpy(buffer, m_cachePtr, length);
219         m_cachePtr += length;
220     } else {
221         size_t sizeToRead = length;
222         size_t offset = 0;
223         while (sizeToRead) {
224             size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
225             offset = length - sizeToRead;
226             memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
227             m_cachePtr += chunkSize;
228             sizeToRead -= chunkSize;
229             if (sizeToRead > 0) {
230                 flushReadCache();
231             }
232             if (!m_cacheSize) {
233                 return length - sizeToRead;
234             }
235         }
236     }
237
238     return length;
239 }
240
241 int SnappyFile::rawGetc()
242 {
243     unsigned char c = 0;
244     if (rawRead(&c, 1) != 1)
245         return -1;
246     return c;
247 }
248
249 void SnappyFile::rawClose()
250 {
251     if (m_mode == File::Write) {
252         flushWriteCache();
253     }
254     m_stream.close();
255     delete [] m_cache;
256     m_cache = NULL;
257     m_cachePtr = NULL;
258 }
259
260 void SnappyFile::rawFlush()
261 {
262     assert(m_mode == File::Write);
263     flushWriteCache();
264     m_stream.flush();
265 }
266
267 void SnappyFile::flushWriteCache()
268 {
269     size_t inputLength = usedCacheSize();
270
271     if (inputLength) {
272         size_t compressedLength;
273
274         ::snappy::RawCompress(m_cache, inputLength,
275                               m_compressedCache, &compressedLength);
276
277         writeCompressedLength(compressedLength);
278         m_stream.write(m_compressedCache, compressedLength);
279         m_cachePtr = m_cache;
280     }
281     assert(m_cachePtr == m_cache);
282 }
283
284 void SnappyFile::flushReadCache(size_t skipLength)
285 {
286     //assert(m_cachePtr == m_cache + m_cacheSize);
287     m_currentOffset.chunk = m_stream.tellg();
288     size_t compressedLength;
289     compressedLength = readCompressedLength();
290
291     if (compressedLength) {
292         m_stream.read((char*)m_compressedCache, compressedLength);
293         ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
294                                         &m_cacheSize);
295         createCache(m_cacheSize);
296         if (skipLength < m_cacheSize) {
297             ::snappy::RawUncompress(m_compressedCache, compressedLength,
298                                     m_cache);
299         }
300     } else {
301         createCache(0);
302     }
303 }
304
305 void SnappyFile::createCache(size_t size)
306 {
307     if (size > m_cacheMaxSize) {
308         do {
309             m_cacheMaxSize <<= 1;
310         } while (size > m_cacheMaxSize);
311
312         delete [] m_cache;
313         m_cache = new char[size];
314         m_cacheMaxSize = size;
315     }
316
317     m_cachePtr = m_cache;
318     m_cacheSize = size;
319 }
320
321 void SnappyFile::writeCompressedLength(size_t length)
322 {
323     unsigned char buf[4];
324     buf[0] = length & 0xff; length >>= 8;
325     buf[1] = length & 0xff; length >>= 8;
326     buf[2] = length & 0xff; length >>= 8;
327     buf[3] = length & 0xff; length >>= 8;
328     assert(length == 0);
329     m_stream.write((const char *)buf, sizeof buf);
330 }
331
332 size_t SnappyFile::readCompressedLength()
333 {
334     unsigned char buf[4];
335     size_t length;
336     m_stream.read((char *)buf, sizeof buf);
337     if (m_stream.fail()) {
338         length = 0;
339     } else {
340         length  =  (size_t)buf[0];
341         length |= ((size_t)buf[1] <<  8);
342         length |= ((size_t)buf[2] << 16);
343         length |= ((size_t)buf[3] << 24);
344     }
345     return length;
346 }
347
348 bool SnappyFile::supportsOffsets() const
349 {
350     return true;
351 }
352
353 File::Offset SnappyFile::currentOffset()
354 {
355     m_currentOffset.offsetInChunk = m_cachePtr - m_cache;
356     return m_currentOffset;
357 }
358
359 void SnappyFile::setCurrentOffset(const File::Offset &offset)
360 {
361     // to remove eof bit
362     m_stream.clear();
363     // seek to the start of a chunk
364     m_stream.seekg(offset.chunk, std::ios::beg);
365     // load the chunk
366     flushReadCache();
367     assert(m_cacheSize >= offset.offsetInChunk);
368     // seek within our cache to the correct location within the chunk
369     m_cachePtr = m_cache + offset.offsetInChunk;
370
371 }
372
373 bool SnappyFile::rawSkip(size_t length)
374 {
375     if (endOfData()) {
376         return false;
377     }
378
379     if (freeCacheSize() >= length) {
380         m_cachePtr += length;
381     } else {
382         size_t sizeToRead = length;
383         while (sizeToRead) {
384             size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
385             m_cachePtr += chunkSize;
386             sizeToRead -= chunkSize;
387             if (sizeToRead > 0) {
388                 flushReadCache(sizeToRead);
389             }
390             if (!m_cacheSize) {
391                 break;
392             }
393         }
394     }
395
396     return true;
397 }
398
399 int SnappyFile::rawPercentRead()
400 {
401     return int(100 * (double(m_stream.tellg()) / double(m_endPos)));
402 }
403
404
405 File* File::createSnappy(void) {
406     return new SnappyFile;
407 }