Add new interface for creating new segments.
[cumulus.git] / store.h
1 /* LBS: An LFS-inspired filesystem backup system
2  * Copyright (C) 2006  Michael Vrable
3  *
4  * Backup data is stored in a collection of objects, which are grouped together
5  * into segments for storage purposes.  This file provides interfaces for
6  * reading and writing objects and segments. */
7
8 #ifndef _LBS_STORE_H
9 #define _LBS_STORE_H
10
11 #include <stdint.h>
12
13 #include <exception>
14 #include <map>
15 #include <string>
16 #include <sstream>
17 #include <vector>
18
19 /* In memory datatype to represent key/value pairs of information, such as file
20  * metadata.  Currently implemented as map<string, string>. */
21 typedef std::map<std::string, std::string> dictionary;
22
23 /* IOException will be thrown if an error occurs while reading or writing in
24  * one of the I/O wrappers.  Depending upon the context; this may be fatal or
25  * not--typically, errors reading/writing the store will be serious, but errors
26  * reading an individual file are less so. */
27 class IOException : public std::exception {
28 private:
29     std::string error;
30 public:
31     explicit IOException(const std::string &err) { error = err; }
32     virtual ~IOException() throw () { }
33     std::string getError() const { return error; }
34 };
35
36 /* OutputStream is an abstract interface for writing data without seeking.
37  * Output could be to a file, to an object within a segment, or even to a
38  * memory buffer to help serialize data. */
39 class OutputStream {
40 public:
41     OutputStream();
42     virtual ~OutputStream() { }
43
44     // Write the given data buffer
45     void write(const void *data, size_t len);
46
47     // Return the total number of bytes written so far
48     int64_t get_pos() const { return bytes_written; }
49
50     // Convenience functions for writing other data types.  Values are always
51     // written out in little-endian order.
52     void write_u8(uint8_t val);
53     void write_u16(uint16_t val);
54     void write_u32(uint32_t val);
55     void write_u64(uint64_t val);
56
57     void write_s32(int32_t val) { write_u32((uint32_t)val); }
58     void write_s64(int64_t val) { write_u64((uint64_t)val); }
59
60     void write_varint(uint64_t val);
61
62     void write_string(const std::string &s);
63     void write_dictionary(const dictionary &d);
64
65 protected:
66     // Function which actually causes a write: must be overridden by
67     // implementation.
68     virtual void write_internal(const void *data, size_t len) = 0;
69
70 private:
71     int64_t bytes_written;
72 };
73
74 /* An OutputStream implementation which writes data to memory and returns the
75  * result as a string. */
76 class StringOutputStream : public OutputStream {
77 public:
78     StringOutputStream();
79     std::string contents() const { return buf.str(); }
80
81 protected:
82     virtual void write_internal(const void *data, size_t len);
83
84 private:
85     std::stringstream buf;
86 };
87
88 /* An OutputStream implementation which writes data via the C stdio layer. */
89 class FileOutputStream : public OutputStream {
90 public:
91     explicit FileOutputStream(FILE *file);
92     virtual ~FileOutputStream();
93
94 protected:
95     virtual void write_internal(const void *data, size_t len);
96
97 private:
98     FILE *f;
99 };
100
101 /* An OutputStream which is simply sends writes to another OutputStream, but
102  * does provide separate tracking of bytes written. */
103 class WrapperOutputStream : public OutputStream {
104 public:
105     explicit WrapperOutputStream(OutputStream &o);
106     virtual ~WrapperOutputStream() { }
107
108 protected:
109     virtual void write_internal(const void *data, size_t len);
110
111 private:
112     OutputStream &real;
113 };
114
115 /* Simple wrappers that encode integers using a StringOutputStream and return
116  * the encoded result. */
117 std::string encode_u16(uint16_t val);
118 std::string encode_u32(uint32_t val);
119 std::string encode_u64(uint64_t val);
120
121 struct uuid {
122     uint8_t bytes[16];
123 };
124
125 /* A class which is used to pack multiple objects into a single segment, with a
126  * lookup table to quickly locate each object.  Call new_object() to get an
127  * OutputStream to which a new object may be written, and optionally
128  * finish_object() when finished writing the current object.  Only one object
129  * may be written to a segment at a time; if multiple objects must be written
130  * concurrently, they must be to different segments. */
131 class SegmentWriter {
132 public:
133     SegmentWriter(OutputStream *output, struct uuid u);
134     ~SegmentWriter();
135
136     struct uuid get_uuid() const { return id; }
137
138     // Start writing out a new object to this segment.
139     OutputStream *new_object();
140     void finish_object();
141
142     // Utility functions for generating and formatting UUIDs for display.
143     static struct uuid generate_uuid();
144     static std::string format_uuid(const struct uuid u);
145
146 private:
147     typedef std::vector<std::pair<int64_t, int64_t> > object_table;
148
149     OutputStream *out;
150     struct uuid id;
151
152     int64_t object_start_offset;
153     OutputStream *object_stream;
154
155     object_table objects;
156 };
157
158 /* A SegmentStore, as the name suggests, is used to store the contents of many
159  * segments.  The SegmentStore internally tracks where data should be placed
160  * (such as a local directory or remote storage), and allows new segments to be
161  * easily created as needed. */
162 class SegmentStore {
163 public:
164     // New segments will be stored in the given directory.
165     SegmentStore(const std::string &path);
166
167     SegmentWriter *new_segment();
168
169 private:
170     std::string directory;
171 };
172
173 #endif // _LBS_STORE_H