]> git.cworth.org Git - apitrace/blob - trace_snappyfile.cpp
Merge remote-tracking branch 'origin/master' into on-demand-loading
[apitrace] / trace_snappyfile.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 #include "trace_snappyfile.hpp"
28
29 #include <snappy.h>
30
31 #include <iostream>
32
33 #include <assert.h>
34 #include <string.h>
35
36 using namespace Trace;
37
38 /*
39  * Snappy file format.
40  * -------------------
41  *
42  * Snappy at its core is just a compressoin algorithm so we're
43  * creating a new file format which uses snappy compression
44  * to hold the trace data.
45  *
46  * The file is composed of a number of chunks, they are:
47  * chunk {
48  *     uint32 - specifying the length of the compressed data
49  *     compressed data, in little endian
50  * }
51  * File can contain any number of such chunks.
52  * The default size of an uncompressed chunk is specified in
53  * SNAPPY_CHUNK_SIZE.
54  *
55  * Note:
56  * Currently the default size for a a to-be-compressed data is
57  * 1mb, meaning that the compressed data will be <= 1mb.
58  * The reason it's 1mb is because it seems
59  * to offer a pretty good compression/disk io speed ratio
60  * but that might change.
61  *
62  */
63
64 SnappyFile::SnappyFile(const std::string &filename,
65                               File::Mode mode)
66     : File(),
67       m_cache(0),
68       m_cachePtr(0),
69       m_cacheSize(0)
70 {
71     size_t maxCompressedLength =
72         snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
73     m_compressedCache = new char[maxCompressedLength];
74 }
75
76 SnappyFile::~SnappyFile()
77 {
78     delete [] m_compressedCache;
79     delete [] m_cache;
80 }
81
82 bool SnappyFile::rawOpen(const std::string &filename, File::Mode mode)
83 {
84     std::ios_base::openmode fmode = std::fstream::binary;
85     if (mode == File::Write) {
86         fmode |= (std::fstream::out | std::fstream::trunc);
87         createCache(SNAPPY_CHUNK_SIZE);
88     } else if (mode == File::Read) {
89         fmode |= std::fstream::in;
90     }
91
92     m_stream.open(filename.c_str(), fmode);
93
94     //read in the initial buffer if we're reading
95     if (m_stream.is_open() && mode == File::Read) {
96         // read the snappy file identifier
97         unsigned char byte1, byte2;
98         m_stream >> byte1;
99         m_stream >> byte2;
100         assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);
101
102         flushCache();
103     } else if (m_stream.is_open() && mode == File::Write) {
104         // write the snappy file identifier
105         m_stream << SNAPPY_BYTE1;
106         m_stream << SNAPPY_BYTE2;
107     }
108     return m_stream.is_open();
109 }
110
111 bool SnappyFile::rawWrite(const void *buffer, size_t length)
112 {
113     if (freeCacheSize() > length) {
114         memcpy(m_cachePtr, buffer, length);
115         m_cachePtr += length;
116     } else if (freeCacheSize() == length) {
117         memcpy(m_cachePtr, buffer, length);
118         m_cachePtr += length;
119         flushCache();
120     } else {
121         int sizeToWrite = length;
122
123         while (sizeToWrite >= freeCacheSize()) {
124             int endSize = freeCacheSize();
125             int offset = length - sizeToWrite;
126             memcpy(m_cachePtr, (const char*)buffer + offset, endSize);
127             sizeToWrite -= endSize;
128             m_cachePtr += endSize;
129             flushCache();
130         }
131         if (sizeToWrite) {
132             int offset = length - sizeToWrite;
133             memcpy(m_cachePtr, (const char*)buffer + offset, sizeToWrite);
134             m_cachePtr += sizeToWrite;
135         }
136     }
137
138     return true;
139 }
140
141 bool SnappyFile::rawRead(void *buffer, size_t length)
142 {
143     if (endOfData()) {
144         return false;
145     }
146
147     if (freeCacheSize() >= length) {
148         memcpy(buffer, m_cachePtr, length);
149         m_cachePtr += length;
150     } else {
151         size_t sizeToRead = length;
152         size_t offset = 0;
153         while (sizeToRead) {
154             size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
155             offset = length - sizeToRead;
156             memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
157             m_cachePtr += chunkSize;
158             sizeToRead -= chunkSize;
159             if (sizeToRead > 0)
160                 flushCache();
161             if (!m_cacheSize)
162                 break;
163         }
164     }
165
166     return true;
167 }
168
169 int SnappyFile::rawGetc()
170 {
171     int c = 0;
172     if (!rawRead(&c, 1))
173         return -1;
174     return c;
175 }
176
177 void SnappyFile::rawClose()
178 {
179     flushCache();
180     m_stream.close();
181     delete [] m_cache;
182     m_cache = NULL;
183     m_cachePtr = NULL;
184 }
185
186 void SnappyFile::rawFlush()
187 {
188     flushCache();
189     m_stream.flush();
190 }
191
192 void SnappyFile::flushCache()
193 {
194     if (m_mode == File::Write) {
195         size_t inputLength = usedCacheSize();
196
197         if (inputLength) {
198             size_t compressedLength;
199
200             ::snappy::RawCompress(m_cache, inputLength,
201                                   m_compressedCache, &compressedLength);
202
203             writeCompressedLength(compressedLength);
204             m_stream.write(m_compressedCache, compressedLength);
205             m_cachePtr = m_cache;
206         }
207         assert(m_cachePtr == m_cache);
208     } else if (m_mode == File::Read) {
209         //assert(m_cachePtr == m_cache + m_cacheSize);
210         m_currentOffset.chunk = m_stream.tellg();
211         size_t compressedLength;
212         compressedLength = readCompressedLength();
213
214         if (compressedLength) {
215             m_stream.read((char*)m_compressedCache, compressedLength);
216             ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
217                                             &m_cacheSize);
218             createCache(m_cacheSize);
219             ::snappy::RawUncompress(m_compressedCache, compressedLength,
220                                     m_cache);
221         } else {
222             createCache(0);
223         }
224     }
225 }
226
227 void SnappyFile::createCache(size_t size)
228 {
229     // TODO: only re-allocate if the current buffer is not big enough
230
231     if (m_cache) {
232         delete [] m_cache;
233     }
234
235     if (size) {
236         m_cache = new char[size];
237     } else {
238         m_cache = NULL;
239     }
240
241     m_cachePtr = m_cache;
242     m_cacheSize = size;
243 }
244
245 void SnappyFile::writeCompressedLength(size_t length)
246 {
247     unsigned char buf[4];
248     buf[0] = length & 0xff; length >>= 8;
249     buf[1] = length & 0xff; length >>= 8;
250     buf[2] = length & 0xff; length >>= 8;
251     buf[3] = length & 0xff; length >>= 8;
252     assert(length == 0);
253     m_stream.write((const char *)buf, sizeof buf);
254 }
255
256 size_t SnappyFile::readCompressedLength()
257 {
258     unsigned char buf[4];
259     size_t length;
260     m_stream.read((char *)buf, sizeof buf);
261     if (m_stream.fail()) {
262         length = 0;
263     } else {
264         length  =  (size_t)buf[0];
265         length |= ((size_t)buf[1] <<  8);
266         length |= ((size_t)buf[2] << 16);
267         length |= ((size_t)buf[3] << 24);
268     }
269     return length;
270 }
271
272 bool SnappyFile::supportsOffsets() const
273 {
274     return true;
275 }
276
277 File::Offset SnappyFile::currentOffset()
278 {
279     m_currentOffset.offsetInChunk = m_cachePtr - m_cache;
280     return m_currentOffset;
281 }
282
283 void SnappyFile::setCurrentOffset(const File::Offset &offset)
284 {
285     // to remove eof bit
286     m_stream.clear();
287     // seek to the start of a chunk
288     m_stream.seekg(offset.chunk, std::ios::beg);
289     // load the chunk
290     flushCache();
291     assert(m_cacheSize >= offset.offsetInChunk);
292     // seek within our cache to the correct location within the chunk
293     m_cachePtr = m_cache + offset.offsetInChunk;
294
295 }
296
297 bool SnappyFile::rawSkip(unsigned length)
298 {
299     if (endOfData()) {
300         return false;
301     }
302
303     if (freeCacheSize() >= length) {
304         m_cachePtr += length;
305     } else {
306         int sizeToRead = length;
307         while (sizeToRead) {
308             int chunkSize = std::min(freeCacheSize(), sizeToRead);
309             m_cachePtr += chunkSize;
310             sizeToRead -= chunkSize;
311             if (sizeToRead > 0)
312                 flushCache();
313             if (!m_cacheSize)
314                 break;
315         }
316     }
317
318     return true;
319 }