Generates a binary patch when supplied with the weak and strong hashes from an unpatched target and a readable stream for the up-to-date data. The blocksize must be the same as the value used to generate remotesignatures.
(datastream, remotesignatures, blocksize=4096)
| 39 | |
| 40 | |
| 41 | def rsyncdelta(datastream, remotesignatures, blocksize=4096): |
| 42 | """ |
| 43 | Generates a binary patch when supplied with the weak and strong |
| 44 | hashes from an unpatched target and a readable stream for the |
| 45 | up-to-date data. The blocksize must be the same as the value |
| 46 | used to generate remotesignatures. |
| 47 | """ |
| 48 | remote_weak, remote_strong = remotesignatures |
| 49 | |
| 50 | match = True |
| 51 | matchblock = -1 |
| 52 | deltaqueue = collections.deque() |
| 53 | |
| 54 | while True: |
| 55 | if match and datastream is not None: |
| 56 | # Whenever there is a match or the loop is running for the first |
| 57 | # time, populate the window using weakchecksum instead of rolling |
| 58 | # through every single byte which takes at least twice as long. |
| 59 | window = collections.deque(bytes(datastream.read(blocksize))) |
| 60 | checksum, a, b = weakchecksum(window) |
| 61 | |
| 62 | try: |
| 63 | # If there are two identical weak checksums in a file, and the |
| 64 | # matching strong hash does not occur at the first match, it will |
| 65 | # be missed and the data sent over. May fix eventually, but this |
| 66 | # problem arises very rarely. |
| 67 | matchblock = remote_weak.index(checksum, matchblock + 1) |
| 68 | stronghash = hashlib.md5(bytes(window)).hexdigest() |
| 69 | matchblock = remote_strong.index(stronghash, matchblock) |
| 70 | |
| 71 | match = True |
| 72 | deltaqueue.append(matchblock) |
| 73 | |
| 74 | if datastream.closed: |
| 75 | break |
| 76 | continue |
| 77 | |
| 78 | except ValueError: |
| 79 | # The weakchecksum did not match |
| 80 | match = False |
| 81 | try: |
| 82 | if datastream: |
| 83 | # Get the next byte and affix to the window |
| 84 | newbyte = ord(datastream.read(1)) |
| 85 | window.append(newbyte) |
| 86 | except TypeError: |
| 87 | # No more data from the file; the window will slowly shrink. |
| 88 | # newbyte needs to be zero from here on to keep the checksum |
| 89 | # correct. |
| 90 | newbyte = 0 |
| 91 | tailsize = datastream.tell() % blocksize |
| 92 | datastream = None |
| 93 | |
| 94 | if datastream is None and len(window) <= tailsize: |
| 95 | # The likelihood that any blocks will match after this is |
| 96 | # nearly nil so call it quits. |
| 97 | deltaqueue.append(window) |
| 98 | break |
nothing calls this directly
no test coverage detected