]> git.cworth.org Git - apitrace/blob - common/trace_file_snappy.cpp
Lower case namespaces.
[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
58 #include <assert.h>
59 #include <string.h>
60
61 #include "trace_file.hpp"
62
63
64 #define SNAPPY_CHUNK_SIZE (1 * 1024 * 1024)
65
66 #define SNAPPY_BYTE1 'a'
67 #define SNAPPY_BYTE2 't'
68
69
70 using namespace trace;
71
72
73 class SnappyFile : public File {
74 public:
75     SnappyFile(const std::string &filename = std::string(),
76                File::Mode mode = File::Read);
77     virtual ~SnappyFile();
78
79     virtual bool supportsOffsets() const;
80     virtual File::Offset currentOffset();
81     virtual void setCurrentOffset(const File::Offset &offset);
82 protected:
83     virtual bool rawOpen(const std::string &filename, File::Mode mode);
84     virtual bool rawWrite(const void *buffer, size_t length);
85     virtual bool rawRead(void *buffer, size_t length);
86     virtual int rawGetc();
87     virtual void rawClose();
88     virtual void rawFlush();
89     virtual bool rawSkip(size_t length);
90     virtual int rawPercentRead();
91
92 private:
93     inline size_t usedCacheSize() const
94     {
95         assert(m_cachePtr >= m_cache);
96         return m_cachePtr - m_cache;
97     }
98     inline size_t freeCacheSize() const
99     {
100         assert(m_cacheSize >= usedCacheSize());
101         if (m_cacheSize > 0) {
102             return m_cacheSize - usedCacheSize();
103         } else {
104             return 0;
105         }
106     }
107     inline bool endOfData() const
108     {
109         return m_stream.eof() && freeCacheSize() == 0;
110     }
111     void flushWriteCache();
112     void flushReadCache(size_t skipLength = 0);
113     void createCache(size_t size);
114     void writeCompressedLength(size_t length);
115     size_t readCompressedLength();
116 private:
117     std::fstream m_stream;
118     char *m_cache;
119     char *m_cachePtr;
120     size_t m_cacheSize;
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_cache(0),
132       m_cachePtr(0),
133       m_cacheSize(0)
134 {
135     size_t maxCompressedLength =
136         snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
137     m_compressedCache = new char[maxCompressedLength];
138 }
139
140 SnappyFile::~SnappyFile()
141 {
142     delete [] m_compressedCache;
143     delete [] m_cache;
144 }
145
146 bool SnappyFile::rawOpen(const std::string &filename, File::Mode mode)
147 {
148     std::ios_base::openmode fmode = std::fstream::binary;
149     if (mode == File::Write) {
150         fmode |= (std::fstream::out | std::fstream::trunc);
151         createCache(SNAPPY_CHUNK_SIZE);
152     } else if (mode == File::Read) {
153         fmode |= std::fstream::in;
154     }
155
156     m_stream.open(filename.c_str(), fmode);
157
158     //read in the initial buffer if we're reading
159     if (m_stream.is_open() && mode == File::Read) {
160         m_stream.seekg(0, std::ios::end);
161         m_endPos = m_stream.tellg();
162         m_stream.seekg(0, std::ios::beg);
163
164         // read the snappy file identifier
165         unsigned char byte1, byte2;
166         m_stream >> byte1;
167         m_stream >> byte2;
168         assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);
169
170         flushReadCache();
171     } else if (m_stream.is_open() && mode == File::Write) {
172         // write the snappy file identifier
173         m_stream << SNAPPY_BYTE1;
174         m_stream << SNAPPY_BYTE2;
175     }
176     return m_stream.is_open();
177 }
178
179 bool SnappyFile::rawWrite(const void *buffer, size_t length)
180 {
181     if (freeCacheSize() > length) {
182         memcpy(m_cachePtr, buffer, length);
183         m_cachePtr += length;
184     } else if (freeCacheSize() == length) {
185         memcpy(m_cachePtr, buffer, length);
186         m_cachePtr += length;
187         flushWriteCache();
188     } else {
189         size_t sizeToWrite = length;
190
191         while (sizeToWrite >= freeCacheSize()) {
192             size_t endSize = freeCacheSize();
193             size_t offset = length - sizeToWrite;
194             memcpy(m_cachePtr, (const char*)buffer + offset, endSize);
195             sizeToWrite -= endSize;
196             m_cachePtr += endSize;
197             flushWriteCache();
198         }
199         if (sizeToWrite) {
200             size_t offset = length - sizeToWrite;
201             memcpy(m_cachePtr, (const char*)buffer + offset, sizeToWrite);
202             m_cachePtr += sizeToWrite;
203         }
204     }
205
206     return true;
207 }
208
209 bool SnappyFile::rawRead(void *buffer, size_t length)
210 {
211     if (endOfData()) {
212         return false;
213     }
214
215     if (freeCacheSize() >= length) {
216         memcpy(buffer, m_cachePtr, length);
217         m_cachePtr += length;
218     } else {
219         size_t sizeToRead = length;
220         size_t offset = 0;
221         while (sizeToRead) {
222             size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
223             offset = length - sizeToRead;
224             memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
225             m_cachePtr += chunkSize;
226             sizeToRead -= chunkSize;
227             if (sizeToRead > 0) {
228                 flushReadCache();
229             }
230             if (!m_cacheSize) {
231                 break;
232             }
233         }
234     }
235
236     return true;
237 }
238
239 int SnappyFile::rawGetc()
240 {
241     int c = 0;
242     if (!rawRead(&c, 1))
243         return -1;
244     return c;
245 }
246
247 void SnappyFile::rawClose()
248 {
249     if (m_mode == File::Write) {
250         flushWriteCache();
251     }
252     m_stream.close();
253     delete [] m_cache;
254     m_cache = NULL;
255     m_cachePtr = NULL;
256 }
257
258 void SnappyFile::rawFlush()
259 {
260     assert(m_mode == File::Write);
261     flushWriteCache();
262     m_stream.flush();
263 }
264
265 void SnappyFile::flushWriteCache()
266 {
267     size_t inputLength = usedCacheSize();
268
269     if (inputLength) {
270         size_t compressedLength;
271
272         ::snappy::RawCompress(m_cache, inputLength,
273                               m_compressedCache, &compressedLength);
274
275         writeCompressedLength(compressedLength);
276         m_stream.write(m_compressedCache, compressedLength);
277         m_cachePtr = m_cache;
278     }
279     assert(m_cachePtr == m_cache);
280 }
281
282 void SnappyFile::flushReadCache(size_t skipLength)
283 {
284     //assert(m_cachePtr == m_cache + m_cacheSize);
285     m_currentOffset.chunk = m_stream.tellg();
286     size_t compressedLength;
287     compressedLength = readCompressedLength();
288
289     if (compressedLength) {
290         m_stream.read((char*)m_compressedCache, compressedLength);
291         ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
292                                         &m_cacheSize);
293         createCache(m_cacheSize);
294         if (skipLength < m_cacheSize) {
295             ::snappy::RawUncompress(m_compressedCache, compressedLength,
296                                     m_cache);
297         }
298     } else {
299         createCache(0);
300     }
301 }
302
303 void SnappyFile::createCache(size_t size)
304 {
305     // TODO: only re-allocate if the current buffer is not big enough
306
307     if (m_cache) {
308         delete [] m_cache;
309     }
310
311     if (size) {
312         m_cache = new char[size];
313     } else {
314         m_cache = NULL;
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 100 * (double(m_stream.tellg()) / double(m_endPos));
402 }
403
404
405 File* File::createSnappy(void) {
406     return new SnappyFile;
407 }
408
409 bool File::isSnappyCompressed(const std::string &filename)
410 {
411     std::fstream stream(filename.c_str(),
412                         std::fstream::binary | std::fstream::in);
413     if (!stream.is_open())
414         return false;
415
416     unsigned char byte1, byte2;
417     stream >> byte1;
418     stream >> byte2;
419     stream.close();
420
421     return (byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);
422 }