| 1 |
// Copyright (c) 2009, Whispersoft s.r.l. |
|---|
| 2 |
// All rights reserved. |
|---|
| 3 |
// |
|---|
| 4 |
// Redistribution and use in source and binary forms, with or without |
|---|
| 5 |
// modification, are permitted provided that the following conditions are |
|---|
| 6 |
// met: |
|---|
| 7 |
// |
|---|
| 8 |
// * Redistributions of source code must retain the above copyright |
|---|
| 9 |
// notice, this list of conditions and the following disclaimer. |
|---|
| 10 |
// * Redistributions in binary form must reproduce the above |
|---|
| 11 |
// copyright notice, this list of conditions and the following disclaimer |
|---|
| 12 |
// in the documentation and/or other materials provided with the |
|---|
| 13 |
// distribution. |
|---|
| 14 |
// * Neither the name of Whispersoft s.r.l. nor the names of its |
|---|
| 15 |
// contributors may be used to endorse or promote products derived from |
|---|
| 16 |
// this software without specific prior written permission. |
|---|
| 17 |
// |
|---|
| 18 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 19 |
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 20 |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|---|
| 21 |
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|---|
| 22 |
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|---|
| 23 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|---|
| 24 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|---|
| 25 |
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|---|
| 26 |
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 27 |
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|---|
| 28 |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 29 |
// |
|---|
| 30 |
// Author: Catalin Popescu |
|---|
| 31 |
|
|---|
| 32 |
// This contains utilities for reading and writing records to / from |
|---|
| 33 |
// a stream. |
|---|
| 34 |
// |
|---|
| 35 |
// The main idea is that we add pieces of information (memory stream data) |
|---|
| 36 |
// to a record stream. We add these in blocks of fixed size (so in case of |
|---|
| 37 |
// corrupted blocks we can skip them). Records may spawn over multiple blocks. |
|---|
| 38 |
// |
|---|
| 39 |
// The reading part builds pieces of file (block_size) and from there extracts |
|---|
| 40 |
// records. |
|---|
| 41 |
// |
|---|
| 42 |
// Is needless to say that in order to work, the reader should be created |
|---|
| 43 |
// with the same record size as the writer. |
|---|
| 44 |
// |
|---|
| 45 |
|
|---|
| 46 |
#ifndef __COMMON_IO_LOGIO_RECORDIO_H__ |
|---|
| 47 |
#define __COMMON_IO_LOGIO_RECORDIO_H__ |
|---|
| 48 |
|
|---|
| 49 |
#include <zlib.h> |
|---|
| 50 |
#include <whisperlib/common/io/buffer/memory_stream.h> |
|---|
| 51 |
#include <whisperlib/common/io/zlib/zlibwrapper.h> |
|---|
| 52 |
|
|---|
| 53 |
namespace io { |
|---|
| 54 |
|
|---|
| 55 |
static const int32 kDefaultRecordBlockSize = 65536; |
|---|
| 56 |
static const int32 kMaximumRecordBlockSize = 0xFFFFFF; |
|---|
| 57 |
|
|---|
| 58 |
class RecordWriter { |
|---|
| 59 |
public: |
|---|
| 60 |
enum { |
|---|
| 61 |
HAS_CONT = 1, |
|---|
| 62 |
IS_ZIPPED = 2, |
|---|
| 63 |
}; |
|---|
| 64 |
|
|---|
| 65 |
RecordWriter(int32 block_size = kDefaultRecordBlockSize, |
|---|
| 66 |
bool deflate = false, |
|---|
| 67 |
float dumpable_percent = .9); |
|---|
| 68 |
~RecordWriter(); |
|---|
| 69 |
|
|---|
| 70 |
// Appends the provided content from in to the record. |
|---|
| 71 |
// Upon a return of true, the value of 'out' is ready to |
|---|
| 72 |
// be appended to the file as a one shot-write. |
|---|
| 73 |
bool AppendRecord(io::MemoryStream* in, io::MemoryStream* out) { |
|---|
| 74 |
return AppendRecord(in, out, false); |
|---|
| 75 |
} |
|---|
| 76 |
bool AppendRecord(const char* buffer, int32 size, io::MemoryStream* out); |
|---|
| 77 |
|
|---|
| 78 |
// This returns the current content (accumulated so far) as a one block |
|---|
| 79 |
// to be written to the disk. |
|---|
| 80 |
void FinalizeContent(io::MemoryStream* out); |
|---|
| 81 |
|
|---|
| 82 |
// In a block we can have at most this much data from a record |
|---|
| 83 |
// We prepend the record size |
|---|
| 84 |
int32 max_block_record_size() const { |
|---|
| 85 |
return block_size_ - kTrailerEnd - sizeof(int32); |
|---|
| 86 |
} |
|---|
| 87 |
|
|---|
| 88 |
int32 leftover() const { |
|---|
| 89 |
return content_.Size(); |
|---|
| 90 |
} |
|---|
| 91 |
private: |
|---|
| 92 |
bool AppendRecord(io::MemoryStream* in, io::MemoryStream* out, |
|---|
| 93 |
bool is_zipped); |
|---|
| 94 |
// We trail with the content size and crc.. |
|---|
| 95 |
static const int32 kTrailerEnd = sizeof(int32) + sizeof(int32); |
|---|
| 96 |
|
|---|
| 97 |
const int32 block_size_; // we write record blocks of this size |
|---|
| 98 |
const int32 dumpable_size_; // we can finish a record if we have more |
|---|
| 99 |
// than this in the buffer and the next |
|---|
| 100 |
// records overflows |
|---|
| 101 |
io::MemoryStream content_; // accumulated content so far.. |
|---|
| 102 |
char* const padding_; // just some zeroes used for padding |
|---|
| 103 |
ZlibDeflateWrapper* zlib_; // for compressing content |
|---|
| 104 |
io::MemoryStream zlib_content_; |
|---|
| 105 |
// buffer of compressed content |
|---|
| 106 |
|
|---|
| 107 |
DISALLOW_EVIL_CONSTRUCTORS(RecordWriter); |
|---|
| 108 |
}; |
|---|
| 109 |
|
|---|
| 110 |
class RecordReader { |
|---|
| 111 |
public: |
|---|
| 112 |
explicit RecordReader(int32 block_size = kDefaultRecordBlockSize); |
|---|
| 113 |
~RecordReader(); |
|---|
| 114 |
|
|---|
| 115 |
// Reads the content of the next record from the provided memory stream |
|---|
| 116 |
enum ReadResult { |
|---|
| 117 |
READ_OK = 0, // out contains some valid data |
|---|
| 118 |
READ_NO_DATA, // there is not enough data in 'in' to be read |
|---|
| 119 |
READ_CRC_CORRUPTED, // the data was corrupted (crc wise) |
|---|
| 120 |
READ_ZIP_CORRUPTED, // the zipped recored was corrupted |
|---|
| 121 |
}; |
|---|
| 122 |
ReadResult ReadRecord(io::MemoryStream* in, io::MemoryStream* out); |
|---|
| 123 |
void Clear() { |
|---|
| 124 |
temp_.Clear(); |
|---|
| 125 |
content_.Clear(); |
|---|
| 126 |
} |
|---|
| 127 |
private: |
|---|
| 128 |
RecordReader::ReadResult ReadNextBlock(io::MemoryStream* in); |
|---|
| 129 |
|
|---|
| 130 |
static const int32 kTrailerEnd = sizeof(int32) + sizeof(int32); |
|---|
| 131 |
|
|---|
| 132 |
const int32 block_size_; // we write records of this size |
|---|
| 133 |
io::MemoryStream temp_; // a temp buffer |
|---|
| 134 |
io::MemoryStream content_; // accumulated content so far.. |
|---|
| 135 |
io::MemoryStream zip_content_; |
|---|
| 136 |
// zipped accumulated content so far.. |
|---|
| 137 |
ZlibInflateWrapper zlib_; // inflates stuff for us |
|---|
| 138 |
|
|---|
| 139 |
DISALLOW_EVIL_CONSTRUCTORS(RecordReader); |
|---|
| 140 |
}; |
|---|
| 141 |
} |
|---|
| 142 |
|
|---|
| 143 |
#endif // __COMMON_IO_LOGIO_RECORDIO_H__ |
|---|