2019-07-07 12:33:25 +00:00
|
|
|
import
|
2022-04-01 10:57:39 +00:00
|
|
|
stew/[leb128, ranges/ptr_arith],
|
2020-05-05 21:35:55 +00:00
|
|
|
faststreams/[inputs, outputs, buffers, multisync],
|
2022-04-01 10:57:39 +00:00
|
|
|
./snappy/[codec, decoder, encoder_fs, types]
|
2020-08-18 20:11:42 +00:00
|
|
|
|
|
|
|
export
|
|
|
|
types
|
2019-07-07 12:33:25 +00:00
|
|
|
|
2020-04-09 20:02:36 +00:00
|
|
|
proc appendSnappyBytes*(s: OutputStream, src: openArray[byte]) =
|
2018-11-02 05:10:58 +00:00
|
|
|
var
|
2022-04-01 10:57:39 +00:00
|
|
|
lenU32 = checkInputLen(src.len).valueOr:
|
|
|
|
raiseInputTooLarge()
|
2018-11-02 05:10:58 +00:00
|
|
|
p = 0
|
2019-07-07 12:33:25 +00:00
|
|
|
|
2020-08-18 20:11:42 +00:00
|
|
|
# The block starts with the varint-encoded length of the decompressed bytes.
|
2020-12-14 11:03:12 +00:00
|
|
|
s.write lenU32.toBytes(Leb128).toOpenArray()
|
2021-12-11 17:05:30 +00:00
|
|
|
if lenU32 <= 0: return
|
2018-11-02 05:10:58 +00:00
|
|
|
|
2022-04-01 10:57:39 +00:00
|
|
|
while lenU32 > maxBlockSize:
|
|
|
|
s.encodeBlock src.toOpenArray(p, p + maxBlockSize.int - 1)
|
|
|
|
p += maxBlockSize.int
|
|
|
|
lenU32 -= maxBlockSize
|
2018-11-02 05:10:58 +00:00
|
|
|
|
2020-08-18 20:11:42 +00:00
|
|
|
# The `lenU32.int` expressions below cannot overflow because
|
|
|
|
# `lenU32` is already less than `maxBlockSize` here:
|
|
|
|
if lenU32 < minNonLiteralBlockSize.uint32:
|
2021-12-11 17:05:30 +00:00
|
|
|
s.emitLiteral src.toOpenArray(p, p + lenU32.int - 1)
|
2020-08-18 20:11:42 +00:00
|
|
|
else:
|
2021-12-11 17:05:30 +00:00
|
|
|
s.encodeBlock src.toOpenArray(p, p + lenU32.int - 1)
|
2018-11-02 05:10:58 +00:00
|
|
|
|
2020-05-05 21:35:55 +00:00
|
|
|
proc snappyCompress*(input: InputStream, output: OutputStream) =
|
|
|
|
try:
|
|
|
|
let inputLen = input.len
|
|
|
|
if inputLen.isSome:
|
2022-04-01 10:57:39 +00:00
|
|
|
let
|
|
|
|
lenU32 = checkInputLen(inputLen.get).valueOr:
|
|
|
|
raiseInputTooLarge()
|
|
|
|
maxCompressed = maxCompressedLen(lenU32).valueOr:
|
|
|
|
raiseInputTooLarge()
|
|
|
|
|
|
|
|
output.ensureRunway maxCompressed
|
2020-12-14 11:03:12 +00:00
|
|
|
output.write lenU32.toBytes(Leb128).toOpenArray()
|
2020-05-05 21:35:55 +00:00
|
|
|
else:
|
|
|
|
# TODO: This is a temporary limitation
|
|
|
|
doAssert false, "snappy requires an input stream with a known length"
|
2020-04-09 20:02:36 +00:00
|
|
|
|
2022-04-01 10:57:39 +00:00
|
|
|
while input.readable(maxBlockSize.int):
|
|
|
|
encodeBlock(output, input.read(maxBlockSize.int))
|
2020-04-09 20:02:36 +00:00
|
|
|
|
2020-05-05 21:35:55 +00:00
|
|
|
let remainingBytes = input.totalUnconsumedBytes
|
|
|
|
if remainingBytes > 0:
|
2020-08-18 20:11:42 +00:00
|
|
|
if remainingBytes < minNonLiteralBlockSize:
|
|
|
|
output.emitLiteral input.read(remainingBytes)
|
|
|
|
else:
|
|
|
|
output.encodeBlock input.read(remainingBytes)
|
2020-05-05 21:35:55 +00:00
|
|
|
finally:
|
|
|
|
close output
|
2019-07-07 12:33:25 +00:00
|
|
|
|
|
|
|
# Encode returns the encoded form of src.
|
2020-04-09 20:02:36 +00:00
|
|
|
func encode*(src: openarray[byte]): seq[byte] =
|
2020-05-05 21:35:55 +00:00
|
|
|
# Memory streams doesn't have side effects:
|
2020-04-09 20:02:36 +00:00
|
|
|
{.noSideEffect.}:
|
2020-05-05 21:35:55 +00:00
|
|
|
let output = memoryOutput()
|
|
|
|
snappyCompress(unsafeMemoryInput(src), output)
|
|
|
|
output.getOutput
|
2018-11-02 05:10:58 +00:00
|
|
|
|
2020-08-18 20:11:42 +00:00
|
|
|
func decode*(src: openArray[byte], maxSize = 0xffffffff'u32): seq[byte] =
|
2020-12-14 11:03:12 +00:00
|
|
|
let (lenU32, bytesRead) = uint32.fromBytes(src, Leb128)
|
2020-08-18 20:11:42 +00:00
|
|
|
if bytesRead <= 0 or lenU32 > maxSize:
|
2018-11-02 05:10:58 +00:00
|
|
|
return
|
|
|
|
|
2020-08-18 20:11:42 +00:00
|
|
|
if lenU32 > 0:
|
|
|
|
when sizeof(uint) == 4:
|
|
|
|
if lenU32 > 0x7fffffff'u32:
|
|
|
|
return
|
|
|
|
# `lenU32.int` cannot overflow because of the extra check above
|
|
|
|
result = newSeq[byte](lenU32.int)
|
|
|
|
let errCode = decode(result, src.toOpenArray(bytesRead, src.len - 1))
|
2018-11-02 05:10:58 +00:00
|
|
|
if errCode != 0: result = @[]
|
2018-11-02 13:36:21 +00:00
|
|
|
|
2020-08-18 20:11:42 +00:00
|
|
|
proc snappyUncompress*(src: openArray[byte], dst: var openArray[byte]): uint32 =
|
2022-04-01 10:57:39 +00:00
|
|
|
let (lenU32, bytesRead) = uint32.fromBytes(src, Leb128)
|
|
|
|
if bytesRead <= 0 or lenU32.BiggestUInt > dst.len.BiggestUInt:
|
2020-08-18 20:11:42 +00:00
|
|
|
return 0
|
2020-03-31 05:21:44 +00:00
|
|
|
|
2022-04-01 10:57:39 +00:00
|
|
|
if lenU32 > 0:
|
2020-08-18 20:11:42 +00:00
|
|
|
# `result.int` cannot overflow here, because we've already
|
|
|
|
# checked that it's smaller than the `dst.len` which is an int.
|
2022-04-01 10:57:39 +00:00
|
|
|
let errCode = decode(dst.toOpenArray(0, lenU32.int - 1),
|
2020-08-18 20:11:42 +00:00
|
|
|
src.toOpenArray(bytesRead, src.len - 1))
|
2020-03-28 09:10:15 +00:00
|
|
|
if errCode != 0:
|
2020-08-18 20:11:42 +00:00
|
|
|
return 0
|
2020-03-28 09:10:15 +00:00
|
|
|
|
2022-04-01 10:57:39 +00:00
|
|
|
return lenU32
|