1 module kafkad.utils.snappy;
2 
3 import std.bitmanip;
4 
5 nothrow extern(C):
6 
7 enum {
8     SNAPPY_OK = 0,
9     SNAPPY_INVALID_INPUT = 1,
10     SNAPPY_BUFFER_TOO_SMALL = 2
11 }
12 
13 int snappy_compress(const ubyte* input,
14     size_t input_length,
15     ubyte* compressed,
16     size_t* compressed_length);
17 
18 int snappy_uncompress(const ubyte* compressed,
19     size_t compressed_length,
20     ubyte* uncompressed,
21     size_t* uncompressed_length);
22 
23 int snappy_uncompressed_length(const ubyte* compressed,
24     size_t compressed_length,
25     size_t* result);
26 
27 int snappy_java_uncompress(const ubyte* compressed,
28     size_t compressed_length,
29     ubyte* uncompressed,
30     size_t* uncompressed_length)
31 {
32     // Snappy-Java adds its own framing: Header [ChunkLen Chunk]
33     // Header = Magic (8 bytes) Version (4 bytes) Compatible (4 bytes)
34     enum headerLen = 8 + 4 + 4;
35     if (compressed_length < headerLen + 4) // header + chunk length
36         return SNAPPY_INVALID_INPUT;
37     // check magic value
38     enum ubyte[8] magic = [0x82, 'S', 'N', 'A', 'P', 'P', 'Y', 0];
39     ubyte* p = cast(ubyte*)compressed;
40     auto cmagic = cast(ubyte[8]*)p;
41     if (*cmagic != magic)
42         return SNAPPY_INVALID_INPUT;
43     p += headerLen;
44     compressed_length -= headerLen;
45     if (!compressed_length)
46         return SNAPPY_INVALID_INPUT;
47     size_t wholeUncompressedLen = 0, chunkCount = 0;
48     while (compressed_length) {
49         if (compressed_length < 4)
50             return SNAPPY_INVALID_INPUT;
51         auto chunkLen = bigEndianToNative!uint(p[0 .. 4]);
52         if (!chunkLen)
53             return SNAPPY_INVALID_INPUT;
54         p += 4;
55         compressed_length -= 4;
56         if (chunkLen > compressed_length)
57             return SNAPPY_INVALID_INPUT;
58         size_t chunkUncompressedLen;
59         if (snappy_uncompressed_length(p, chunkLen, &chunkUncompressedLen) != SNAPPY_OK || !chunkUncompressedLen)
60             return SNAPPY_INVALID_INPUT;
61         wholeUncompressedLen += chunkUncompressedLen;
62         p += chunkLen;
63         compressed_length -= chunkLen;
64         ++chunkCount;
65     }
66     if (wholeUncompressedLen > *uncompressed_length)
67         return SNAPPY_BUFFER_TOO_SMALL;
68     p = cast(ubyte*)compressed + headerLen;
69     while (chunkCount--) {
70         auto chunkLen = bigEndianToNative!uint(p[0 .. 4]);
71         p += 4;
72         size_t rem = *uncompressed_length;
73         if (snappy_uncompress(p, chunkLen, uncompressed, &rem) != SNAPPY_OK)
74             return SNAPPY_INVALID_INPUT;
75         p += chunkLen;
76         uncompressed += rem;
77         *uncompressed_length -= rem;
78     }
79     *uncompressed_length = wholeUncompressedLen;
80     return SNAPPY_OK;
81 }