summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2010-07-10 18:02:15 +1200
committerLars Wirzenius <liw@liw.fi>2010-07-10 18:02:15 +1200
commit033385f7d6f5f603d5f7d9816bb15ed2c3bcf334 (patch)
tree74b53935bee613e3f4e2afd50563b00642841622
parentfe51b60533bd8b730597aa5b4c6fbd69b6e0cb21 (diff)
downloadobnam-033385f7d6f5f603d5f7d9816bb15ed2c3bcf334.tar.gz
Implement metadata coding with struct instead of pickle.
This is more portable to implementations in other languages, if we ever need it.
-rw-r--r--obnamlib/store.py41
-rw-r--r--obnamlib/store_tests.py14
2 files changed, 51 insertions, 4 deletions
diff --git a/obnamlib/store.py b/obnamlib/store.py
index 8e6226b5..314b4257 100644
--- a/obnamlib/store.py
+++ b/obnamlib/store.py
@@ -23,7 +23,6 @@ import btree
import errno
import hashlib
import os
-import pickle
import random
import struct
import time
@@ -84,12 +83,48 @@ def require_started_generation(method):
return helper
+numeric_fields = [x for x in obnamlib.metadata_fields if x.startswith('st_')]
+string_fields = [x for x in obnamlib.metadata_fields
+ if x not in numeric_fields]
+num_numeric = len(numeric_fields)
+metadata_format = struct.Struct('!Q' + 'Q' * len(obnamlib.metadata_fields))
+
+
def encode_metadata(metadata):
- return pickle.dumps(metadata)
+ flags = 0
+ for i, name in enumerate(obnamlib.metadata_fields):
+ if getattr(metadata, name) is not None:
+ flags |= (1 << i)
+ fields = ([flags] +
+ [getattr(metadata, x) or 0 for x in numeric_fields] +
+ [len(getattr(metadata, x) or '') for x in string_fields])
+ string = ''.join(getattr(metadata, x) or '' for x in string_fields)
+ return metadata_format.pack(*fields) + string
def decode_metadata(encoded):
- return pickle.loads(encoded)
+ buf = buffer(encoded)
+ items = metadata_format.unpack_from(buf)
+
+ flags = items[0]
+ values = list(items[1:len(numeric_fields)+1])
+ lengths = items[len(numeric_fields)+1:]
+
+ offset = metadata_format.size
+ append = values.append
+ for length in lengths:
+ append(encoded[offset:offset + length])
+ offset += length
+
+ def flagtonone(values):
+ for i, value in enumerate(values):
+ if flags & (1 << i):
+ yield value
+ else:
+ yield None
+
+ args = dict(zip(numeric_fields + string_fields, flagtonone(values)))
+ return obnamlib.Metadata(**args)
class NodeStoreVfs(btree.NodeStoreDisk):
diff --git a/obnamlib/store_tests.py b/obnamlib/store_tests.py
index ae6210fa..75f56bfc 100644
--- a/obnamlib/store_tests.py
+++ b/obnamlib/store_tests.py
@@ -45,7 +45,19 @@ class MetadataCodingTests(unittest.TestCase):
for name in dir(metadata):
if name in obnamlib.metadata.metadata_fields:
self.assertEqual(getattr(metadata, name),
- getattr(decoded, name))
+ getattr(decoded, name),
+ 'attribute %s must be equal (%s vs %s)' %
+ (name, getattr(metadata, name),
+ getattr(decoded, name)))
+
+ def test_round_trip_for_None_values(self):
+ metadata = obnamlib.metadata.Metadata()
+ encoded = obnamlib.store.encode_metadata(metadata)
+ decoded = obnamlib.store.decode_metadata(encoded)
+ for name in dir(metadata):
+ if name in obnamlib.metadata.metadata_fields:
+ self.assertEqual(getattr(decoded, name), None,
+ 'attribute %s must be None' % name)
class ChecksumTreeTests(unittest.TestCase):