Change syntax for size-assertion slices. This is an incompatible change.
authorMichael Vrable <vrable@cs.hmc.edu>
Wed, 5 Feb 2014 04:20:09 +0000 (20:20 -0800)
committerMichael Vrable <vrable@cs.hmc.edu>
Wed, 5 Feb 2014 04:20:09 +0000 (20:20 -0800)
Simplify the syntax by replacing the [=nnn] syntax with just [nnn].  Slices
that do not specify a size are written [0+nnn]; the old backup tool has
been doing this already since about v0.8.  Before that point, [nnn] was a
shorthand for [0+nnn], and will be mis-interpreted by modern tools.

NEWS
doc/format.txt
python/cumulus/__init__.py
ref.cc

diff --git a/NEWS b/NEWS
index eeb15be..1dbe28c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
 0.11 [???]
     - SNAPSHOT FORMAT CHANGES:
+        - The object slice syntax has been simplified; backup restores
+          are still possible but rebuilding the local database from
+          snapshots before v0.8 will require translation.
         - Timestamps encoded into the top-level snapshot file name are
           now UTC instead of the local time zone.  This should avoid
           ambiguity and ensure sorted order is chronological order in
index 19bf2f2..2c40075 100644 (file)
@@ -149,18 +149,20 @@ is invalid to select using the slice syntax a range of bytes that does
 not fall within the original object.  The slice specification should be
 appended to an object name, for example:
     a704eeae-97f2-4f30-91a4-d4473956366b/000001ad[264+1000]
-selects only bytes 264..1263 from the original object.  As an
-abbreviation, the slice syntax
+selects only bytes 264..1263 from the original object.
+
+The slice syntax
     [<length>]
-is shorthand for
-    [0+<length>]
-In place of a traditional slice, the annotation
-    [=<length>]
-may be used.  This is somewhat similar to specifying [<length>], but
+indicates that all bytes of the object are to be used, but
 additionally asserts that the referenced object is exactly <length>
-bytes long--that is, this slice syntax does not change the bytes
-returned at all, but can be used to provide information about the
-underlying object store.
+bytes long.  Older versions of Cumulus can also use the syntax
+    [=<length>]
+as a synonym for length assertions, but this notation is deprecated.
+
+(In older versions of the format, the syntax [<length>] was a shorthand
+for [0+<length>]: that is, select the first <length> bytes of the object
+but make no assertions about the overall size.  The backup tool has not
+generated such slices since v0.8.)
 
 Both a checksum and a slice can be used.  In this case, the checksum is
 given first, followed by the slice.  The checksum is computed over the
@@ -176,7 +178,7 @@ object must have a slice specification appended to indicate the size of
 the object.  For example
     zero[1024]
 represents a block consisting of 1024 null bytes.  A checksum should not
-be given.  The slice syntax should use the abbreviated length-only form.
+be given.
 
 
 FILE METADATA LISTING
index c5961c7..323a7c7 100644 (file)
@@ -343,7 +343,7 @@ class CumulusStore:
         if m:
             return ("zero", None, None, (0, int(m.group(1)), False))
 
-        m = re.match(r"^([-0-9a-f]+)\/([0-9a-f]+)(\(\S+\))?(\[(((\d+)\+)?(\d+)|=(\d+))\])?$", refstr)
+        m = re.match(r"^([-0-9a-f]+)\/([0-9a-f]+)(\(\S+\))?(\[(=?(\d+)|(\d+)\+(\d+))\])?$", refstr)
         if not m: return
 
         segment = m.group(1)
@@ -355,12 +355,9 @@ class CumulusStore:
             checksum = checksum.lstrip("(").rstrip(")")
 
         if slice is not None:
-            if m.group(9) is not None:
+            if m.group(6) is not None:
                 # Size-assertion slice
-                slice = (0, int(m.group(9)), True)
-            elif m.group(6) is None:
-                # Abbreviated slice
-                slice = (0, int(m.group(8)), False)
+                slice = (0, int(m.group(6)), True)
             else:
                 slice = (int(m.group(7)), int(m.group(8)), False)
 
@@ -451,6 +448,9 @@ class CumulusStore:
 
         if slice is not None:
             (start, length, exact) = slice
+            # Note: The following assertion check may need to be commented out
+            # to restore from pre-v0.8 snapshots, as the syntax for
+            # size-assertion slices has changed.
             if exact and len(data) != length: raise ValueError
             data = data[start:start+length]
             if len(data) != length: raise IndexError
diff --git a/ref.cc b/ref.cc
index c5a7768..2a45781 100644 (file)
--- a/ref.cc
+++ b/ref.cc
@@ -97,9 +97,7 @@ string ObjectReference::to_string() const
 
     if (range_valid) {
         char buf[64];
-        if (range_exact) {
-            sprintf(buf, "[=%zu]", range_length);
-        } else if (type == REF_ZERO) {
+        if (range_exact || type == REF_ZERO) {
             sprintf(buf, "[%zu]", range_length);
         } else {
             sprintf(buf, "[%zu+%zu]", range_start, range_length);
@@ -166,6 +164,9 @@ ObjectReference ObjectReference::parse(const std::string& str)
     if (*t == '[') {
         t++;
 
+        // An equal sign was once used for a length assertion but is now
+        // deprecated.  Skip it if present, and mark that we are expecting a
+        // length-only reference.
         if (*t == '=') {
             range_exact = true;
             t++;
@@ -179,6 +180,7 @@ ObjectReference ObjectReference::parse(const std::string& str)
         if (*t == ']') {
             string val(s, t - s);
             range2 = atoll(val.c_str());
+            range_exact = true;
         } else {
             if (*t != '+')
                 return ObjectReference();