Encodes the given data with snappy compression. If xerial_compatible is set then the stream is encoded in a fashion compatible with the xerial snappy library. The block size (xerial_blocksize) controls how frequent the blocking occurs 32k is the default in the xerial library.
(payload, xerial_compatible=True, xerial_blocksize=32*1024)
| 104 | |
| 105 | |
| 106 | def snappy_encode(payload, xerial_compatible=True, xerial_blocksize=32*1024): |
| 107 | """Encodes the given data with snappy compression. |
| 108 | |
| 109 | If xerial_compatible is set then the stream is encoded in a fashion |
| 110 | compatible with the xerial snappy library. |
| 111 | |
| 112 | The block size (xerial_blocksize) controls how frequent the blocking occurs |
| 113 | 32k is the default in the xerial library. |
| 114 | |
| 115 | The format winds up being: |
| 116 | |
| 117 | |
| 118 | +-------------+------------+--------------+------------+--------------+ |
| 119 | | Header | Block1 len | Block1 data | Blockn len | Blockn data | |
| 120 | +-------------+------------+--------------+------------+--------------+ |
| 121 | | 16 bytes | BE int32 | snappy bytes | BE int32 | snappy bytes | |
| 122 | +-------------+------------+--------------+------------+--------------+ |
| 123 | |
| 124 | |
| 125 | It is important to note that the blocksize is the amount of uncompressed |
| 126 | data presented to snappy at each block, whereas the blocklen is the number |
| 127 | of bytes that will be present in the stream; so the length will always be |
| 128 | <= blocksize. |
| 129 | |
| 130 | """ |
| 131 | |
| 132 | if not has_snappy(): |
| 133 | raise NotImplementedError("Snappy codec is not available") |
| 134 | |
| 135 | if not xerial_compatible: |
| 136 | return snappy.compress(payload) |
| 137 | |
| 138 | out = io.BytesIO() |
| 139 | for fmt, dat in zip(_XERIAL_V1_FORMAT, _XERIAL_V1_HEADER): |
| 140 | out.write(struct.pack('!' + fmt, dat)) |
| 141 | |
| 142 | # Chunk through buffers to avoid creating intermediate slice copies |
| 143 | if PYPY: |
| 144 | # on pypy, snappy.compress() on a sliced buffer consumes the entire |
| 145 | # buffer... likely a python-snappy bug, so just use a slice copy |
| 146 | chunker = lambda payload, i, size: payload[i:size+i] |
| 147 | |
| 148 | else: |
| 149 | # snappy.compress does not like raw memoryviews, so we have to convert |
| 150 | # tobytes, which is a copy... oh well. it's the thought that counts. |
| 151 | # pylint: disable-msg=undefined-variable |
| 152 | chunker = lambda payload, i, size: memoryview(payload)[i:size+i].tobytes() |
| 153 | |
| 154 | for chunk in (chunker(payload, i, xerial_blocksize) |
| 155 | for i in range(0, len(payload), xerial_blocksize)): |
| 156 | |
| 157 | block = snappy.compress(chunk) |
| 158 | block_size = len(block) |
| 159 | out.write(struct.pack('!i', block_size)) |
| 160 | out.write(block) |
| 161 | |
| 162 | return out.getvalue() |
| 163 |
searching dependent graphs…