+def object_data_response(request, sizes, hashmaps, meta, public=False):
+ """Get the HttpResponse object for replying with the object's data."""
+
+ # Range handling.
+ size = sum(sizes)
+ ranges = get_range(request, size)
+ if ranges is None:
+ ranges = [(0, size)]
+ ret = 200
+ else:
+ check = [True for offset, length in ranges if
+ length <= 0 or length > size or
+ offset < 0 or offset >= size or
+ offset + length > size]
+ if len(check) > 0:
+ raise RangeNotSatisfiable('Requested range exceeds object limits')
+ ret = 206
+ if_range = request.META.get('HTTP_IF_RANGE')
+ if if_range:
+ try:
+ # Modification time has passed instead.
+ last_modified = parse_http_date(if_range)
+ if last_modified != meta['modified']:
+ ranges = [(0, size)]
+ ret = 200
+ except ValueError:
+ if if_range != meta['hash']:
+ ranges = [(0, size)]
+ ret = 200
+
+ if ret == 206 and len(ranges) > 1:
+ boundary = uuid.uuid4().hex
+ else:
+ boundary = ''
+ wrapper = ObjectWrapper(request.backend, ranges, sizes, hashmaps, boundary)
+ response = HttpResponse(wrapper, status=ret)
+ put_object_headers(response, meta, public)
+ if ret == 206:
+ if len(ranges) == 1:
+ offset, length = ranges[0]
+ response['Content-Length'] = length # Update with the correct length.
+ response['Content-Range'] = 'bytes %d-%d/%d' % (offset, offset + length - 1, size)
+ else:
+ del(response['Content-Length'])
+ response['Content-Type'] = 'multipart/byteranges; boundary=%s' % (boundary,)
+ return response
+
+def put_object_block(request, hashmap, data, offset):
+ """Put one block of data at the given offset."""
+
+ bi = int(offset / request.backend.block_size)
+ bo = offset % request.backend.block_size
+ bl = min(len(data), request.backend.block_size - bo)
+ if bi < len(hashmap):
+ hashmap[bi] = request.backend.update_block(hashmap[bi], data[:bl], bo)
+ else:
+ hashmap.append(request.backend.put_block(('\x00' * bo) + data[:bl]))
+ return bl # Return ammount of data written.
+
+def hashmap_hash(request, hashmap):
+ """Produce the root hash, treating the hashmap as a Merkle-like tree."""
+
+ def subhash(d):
+ h = hashlib.new(request.backend.hash_algorithm)
+ h.update(d)
+ return h.digest()
+
+ if len(hashmap) == 0:
+ return hexlify(subhash(''))
+ if len(hashmap) == 1:
+ return hashmap[0]
+
+ s = 2
+ while s < len(hashmap):
+ s = s * 2
+ h = [unhexlify(x) for x in hashmap]
+ h += [('\x00' * len(h[0]))] * (s - len(hashmap))
+ while len(h) > 1:
+ h = [subhash(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
+ return hexlify(h[0])
+