Assorted minor code cleanups.
[cumulus.git] / ref.cc
1 /* LBS: An LFS-inspired filesystem backup system
2  * Copyright (C) 2007  Michael Vrable
3  *
4  * Backups are structured as a collection of objects, which may refer to other
5  * objects.  Object references are used to name other objects or parts of them.
6  * This file defines the class for representing object references and the
7  * textual representation of these references. */
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <uuid/uuid.h>
12
13 #include <string>
14
15 #include "ref.h"
16
17 using std::string;
18
19 /* Generate a new UUID, and return the text representation of it.  This is
20  * suitable for generating the name for a new segment. */
21 string generate_uuid()
22 {
23     uint8_t uuid[16];
24     char buf[40];
25
26     uuid_generate(uuid);
27     uuid_unparse_lower(uuid, buf);
28     return string(buf);
29 }
30
31 ObjectReference::ObjectReference()
32     : segment(""), object("")
33 {
34 }
35
36 ObjectReference::ObjectReference(const std::string& segment, int sequence)
37     : segment(segment)
38 {
39     char seq_buf[64];
40     sprintf(seq_buf, "%08x", sequence);
41     object = seq_buf;
42
43     clear_checksum();
44     clear_range();
45 }
46
47 ObjectReference::ObjectReference(const std::string& segment,
48                                  const std::string& sequence)
49     : segment(segment), object(sequence)
50 {
51     clear_checksum();
52     clear_range();
53 }
54
55 string ObjectReference::to_string() const
56 {
57     if (is_null())
58         return "/";
59
60     string result = segment + "/" + object;
61
62     if (checksum_valid)
63         result += "(" + checksum + ")";
64
65     if (range_valid) {
66         char buf[64];
67         sprintf(buf, "[%zu+%zu]", range_start, range_length);
68         result += buf;
69     }
70
71     return result;
72 }
73
74 /* Parse a string object reference and return a pointer to a new
75  * ObjectReference.  The caller is responsible for freeing the object.  NULL is
76  * returned if there is an error in the syntax. */
77 ObjectReference ObjectReference::parse(const std::string& str)
78 {
79     const char *s = str.c_str();
80     const char *t;
81
82     // Segment
83     t = s;
84     while ((*t >= '0' && *t <= '9') || (*t >= 'a' && *t <= 'f') || (*t == '-'))
85         t++;
86     if (*t != '/')
87         return ObjectReference();
88     string segment(s, t - s);
89
90     // Object sequence number
91     t++;
92     s = t;
93     while ((*t >= '0' && *t <= '9') || (*t >= 'a' && *t <= 'f'))
94         t++;
95     if (*t != '\0' && *t != '(' && *t != '[')
96         return ObjectReference();
97     string object(s, t - s);
98
99     // Checksum
100     string checksum;
101     if (*t == '(') {
102         t++;
103         s = t;
104         while (*t != ')' && *t != '\0')
105             t++;
106         if (*t != ')')
107             return ObjectReference();
108         checksum = string(s, t - s);
109         t++;
110     }
111
112     // Range
113     bool have_range = false;
114     int64_t range1, range2;
115     if (*t == '[') {
116         t++;
117         s = t;
118         while (*t >= '0' && *t <= '9')
119             t++;
120         if (*t != '+')
121             return ObjectReference();
122
123         string val(s, t - s);
124         range1 = atoll(val.c_str());
125
126         t++;
127         s = t;
128         while (*t >= '0' && *t <= '9')
129             t++;
130         if (*t != ']')
131             return ObjectReference();
132
133         val = string(s, t - s);
134         range2 = atoll(val.c_str());
135
136         have_range = true;
137     }
138
139     ObjectReference ref(segment, object);
140     if (checksum.size() > 0)
141         ref.set_checksum(checksum);
142
143     if (have_range)
144         ref.set_range(range1, range2);
145
146     return ref;
147 }
148
149 /* Attempt to merge a new object reference into the current one.  Returns a
150  * boolean indicating success; if successful this reference is modified so that
151  * it refers to the range of bytes originally covered by this reference plus
152  * the reference passed in.  Merging only succeeds if both references refer to
153  * the same object and the byte ranges are contiguous. */
154 bool ObjectReference::merge(ObjectReference ref)
155 {
156     // Exception: We can always merge into a null object
157     if (is_null()) {
158         *this = ref;
159         return true;
160     }
161
162     if (segment != ref.segment)
163         return false;
164     if (object != ref.object)
165         return false;
166
167     // TODO: Allow the case where only one checksum was filled in
168     if (checksum_valid != ref.checksum_valid || checksum != ref.checksum)
169         return false;
170
171     if (!range_valid || !ref.range_valid)
172         return false;
173
174     if (range_start + range_length == ref.range_start) {
175         range_length += ref.range_length;
176         return true;
177     } else {
178         return false;
179     }
180 }