# Path: python/boto/glacier/exceptions.py
# class TreeHashDoesNotMatchError(DownloadArchiveError):
#     pass
# 
# class DownloadArchiveError(Exception):
#     pass
# 
# Path: python/boto/glacier/writer.py
# def bytes_to_hex(str):
#     return ''.join(["%02x" % ord(x) for x in str]).strip()
# 
# def chunk_hashes(bytestring, chunk_size=_ONE_MEGABYTE):
#     chunk_count = int(math.ceil(len(bytestring) / float(chunk_size)))
#     hashes = []
#     for i in xrange(chunk_count):
#         start = i * chunk_size
#         end = (i + 1) * chunk_size
#         hashes.append(hashlib.sha256(bytestring[start:end]).digest())
#     return hashes
# 
# def tree_hash(fo):
#     """
#     Given a hash of each 1MB chunk (from chunk_hashes) this will hash
#     together adjacent hashes until it ends up with one big one. So a
#     tree of hashes.
#     """
#     hashes = []
#     hashes.extend(fo)
#     while len(hashes) > 1:
#         new_hashes = []
#         while True:
#             if len(hashes) > 1:
#                 first = hashes.pop(0)
#                 second = hashes.pop(0)
#                 new_hashes.append(hashlib.sha256(first + second).digest())
#             elif len(hashes) == 1:
#                 only = hashes.pop(0)
#                 new_hashes.append(only)
#             else:
#                 break
#         hashes.extend(new_hashes)
#     return hashes[0]


import math
import socket
from .exceptions import TreeHashDoesNotMatchError, DownloadArchiveError
from .writer import bytes_to_hex, chunk_hashes, tree_hash

    def download_to_file(self, filename, chunk_size=DefaultPartSize,
                         verify_hashes=True, retry_exceptions=(socket.error,)):
        """Download an archive to a file.

        :type filename: str
        :param filename: The name of the file where the archive
            contents will be saved.

        :type chunk_size: int
        :param chunk_size: The chunk size to use when downloading
            the archive.

        :type verify_hashes: bool
        :param verify_hashes: Indicates whether or not to verify
            the tree hashes for each downloaded chunk.

        """
        num_chunks = int(math.ceil(self.archive_size / float(chunk_size)))
        with open(filename, 'wb') as output_file:
            self._download_to_fileob(output_file, num_chunks, chunk_size,
                                     verify_hashes, retry_exceptions)

    def _download_to_fileob(self, fileobj, num_chunks, chunk_size, verify_hashes,
                            retry_exceptions):
        for i in xrange(num_chunks):
            byte_range = ((i * chunk_size), ((i + 1) * chunk_size) - 1)
            data, expected_tree_hash = self._download_byte_range(
                byte_range, retry_exceptions)
            if verify_hashes:

# Path: python/boto/glacier/exceptions.py
# class TreeHashDoesNotMatchError(DownloadArchiveError):
#     pass
# 
# class DownloadArchiveError(Exception):
#     pass
# 
# Path: python/boto/glacier/writer.py
# def bytes_to_hex(str):
#     return ''.join(["%02x" % ord(x) for x in str]).strip()
# 
# def chunk_hashes(bytestring, chunk_size=_ONE_MEGABYTE):
#     chunk_count = int(math.ceil(len(bytestring) / float(chunk_size)))
#     hashes = []
#     for i in xrange(chunk_count):
#         start = i * chunk_size
#         end = (i + 1) * chunk_size
#         hashes.append(hashlib.sha256(bytestring[start:end]).digest())
#     return hashes
# 
# def tree_hash(fo):
#     """
#     Given a hash of each 1MB chunk (from chunk_hashes) this will hash
#     together adjacent hashes until it ends up with one big one. So a
#     tree of hashes.
#     """
#     hashes = []
#     hashes.extend(fo)
#     while len(hashes) > 1:
#         new_hashes = []
#         while True:
#             if len(hashes) > 1:
#                 first = hashes.pop(0)
#                 second = hashes.pop(0)
#                 new_hashes.append(hashlib.sha256(first + second).digest())
#             elif len(hashes) == 1:
#                 only = hashes.pop(0)
#                 new_hashes.append(only)
#             else:
#                 break
#         hashes.extend(new_hashes)
#     return hashes[0]


# Path: python/boto/glacier/job.py
import math
import socket
from .exceptions import TreeHashDoesNotMatchError, DownloadArchiveError
from .writer import bytes_to_hex, chunk_hashes, tree_hash


    def download_to_file(self, filename, chunk_size=DefaultPartSize,
                         verify_hashes=True, retry_exceptions=(socket.error,)):
        """Download an archive to a file.

        :type filename: str
        :param filename: The name of the file where the archive
            contents will be saved.

        :type chunk_size: int
        :param chunk_size: The chunk size to use when downloading
            the archive.

        :type verify_hashes: bool
        :param verify_hashes: Indicates whether or not to verify
            the tree hashes for each downloaded chunk.

        """
        num_chunks = int(math.ceil(self.archive_size / float(chunk_size)))
        with open(filename, 'wb') as output_file:
            self._download_to_fileob(output_file, num_chunks, chunk_size,
                                     verify_hashes, retry_exceptions)

    def _download_to_fileob(self, fileobj, num_chunks, chunk_size, verify_hashes,
                            retry_exceptions):
        for i in xrange(num_chunks):
            byte_range = ((i * chunk_size), ((i + 1) * chunk_size) - 1)
            data, expected_tree_hash = self._download_byte_range(
                byte_range, retry_exceptions)
            if verify_hashes:

                actual_tree_hash = bytes_to_hex(tree_hash(chunk_hashes(data)))