X-Git-Url: http://git.vrable.net/?a=blobdiff_plain;f=store.cc;h=168679819d5b9982e4a3aaeefc6fa34792d230d7;hb=4a6945983fa171fa843c6f8955ba601f733f3ca5;hp=5df4b7797d4a377b21f8ba5995d276938c7fc354;hpb=38c66f088ed65d2f42264c92add6e0b33eac2bfc;p=cumulus.git diff --git a/store.cc b/store.cc index 5df4b77..1686798 100644 --- a/store.cc +++ b/store.cc @@ -6,11 +6,23 @@ * reading and writing objects and segments. */ #include +#include #include "store.h" using std::string; +OutputStream::OutputStream() + : bytes_written(0) +{ +} + +void OutputStream::write(const void *data, size_t len) +{ + write_internal(data, len); + bytes_written += len; +} + void OutputStream::write_u8(uint8_t val) { write(&val, 1); @@ -95,7 +107,7 @@ StringOutputStream::StringOutputStream() { } -void StringOutputStream::write(const void *data, size_t len) +void StringOutputStream::write_internal(const void *data, size_t len) { buf.write((const char *)data, len); if (!buf.good()) @@ -112,7 +124,7 @@ FileOutputStream::~FileOutputStream() fclose(f); } -void FileOutputStream::write(const void *data, size_t len) +void FileOutputStream::write_internal(const void *data, size_t len) { size_t res; @@ -122,6 +134,16 @@ void FileOutputStream::write(const void *data, size_t len) } } +WrapperOutputStream::WrapperOutputStream(OutputStream &o) + : real(o) +{ +} + +void WrapperOutputStream::write_internal(const void *data, size_t len) +{ + real.write(data, len); +} + /* Utility functions, for encoding data types to strings. */ string encode_u16(uint16_t val) { @@ -143,3 +165,99 @@ string encode_u64(uint64_t val) s.write_u64(val); return s.contents(); } + +SegmentWriter::SegmentWriter(OutputStream *output, struct uuid u) + : out(output), + id(u), + object_stream(NULL) +{ + /* Write out the segment header first. */ + static const char signature[] = "LBSSEG0\n"; + out->write(signature, strlen(signature)); + out->write(id.bytes, sizeof(struct uuid)); +} + +SegmentWriter::~SegmentWriter() +{ + if (object_stream) + finish_object(); + + // Write out the object table which gives the sizes and locations of all + // objects, and then add the trailing signature, which indicates the end of + // the segment and gives the offset of the object table. + int64_t index_offset = out->get_pos(); + + for (object_table::const_iterator i = objects.begin(); + i != objects.end(); ++i) { + out->write_s64(i->first); + out->write_s64(i->second); + } + + static const char signature2[] = "LBSEND"; + out->write(signature2, strlen(signature2)); + out->write_s64(index_offset); + out->write_u32(objects.size()); + + /* The SegmentWriter takes ownership of the OutputStream it is writing to, + * and destroys it automatically when done with the segment. */ + delete out; +} + +OutputStream *SegmentWriter::new_object() +{ + if (object_stream) + finish_object(); + + object_start_offset = out->get_pos(); + object_stream = new WrapperOutputStream(*out); + + return object_stream; +} + +void SegmentWriter::finish_object() +{ + assert(object_stream != NULL); + + // store (start, length) information for locating this object + objects.push_back(std::make_pair(object_start_offset, + object_stream->get_pos())); + + delete object_stream; + object_stream = NULL; +} + +struct uuid SegmentWriter::generate_uuid() +{ + struct uuid u; + + uuid_generate(u.bytes); + + return u; +} + +string SegmentWriter::format_uuid(const struct uuid u) +{ + // A UUID only takes 36 bytes, plus the trailing '\0', so this is safe. + char buf[40]; + + uuid_unparse_lower(u.bytes, buf); + + return string(buf); +} + +SegmentStore::SegmentStore(const string &path) + : directory(path) +{ +} + +SegmentWriter *SegmentStore::new_segment() +{ + struct uuid id = SegmentWriter::generate_uuid(); + string filename = directory + "/" + SegmentWriter::format_uuid(id); + + FILE *f = fopen(filename.c_str(), "wb"); + if (f == NULL) + throw IOException("Unable to open new segment"); + + return new SegmentWriter(new FileOutputStream(f), id); +}