This repository was archived by the owner on Jul 25, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathSnappyCodec.cs
More file actions
218 lines (209 loc) · 11.6 KB
/
Copy pathSnappyCodec.cs
File metadata and controls
218 lines (209 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// Part of Snappy for Windows: https://snappy.machinezoo.com/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Snappy
{
/// <summary>
/// Straightforward wrapper around the underlying native Snappy library.
/// You can compress, uncompress, and validate fixed-size buffers without framing overhead.
/// Methods of this class forward their parameters directly to the native code without unnecessary buffer copying.
/// </summary>
public static class SnappyCodec
{
/// <summary>
/// Compresses byte buffer using Snappy compression.
/// </summary>
/// <param name="input">Input buffer containing data to be compressed.</param>
/// <param name="offset">Offset into the input buffer where input data is located.</param>
/// <param name="length">Size of input data in the input buffer. Zero-length input is allowed. There's no maximum input size.</param>
/// <param name="output">
/// Output buffer where compressed data will be stored.
/// Buffer length minus outOffset must be equal or higher than return value of GetMaxCompressedLength method.
/// </param>
/// <param name="outOffset">Offset into the output buffer where compressed data will be stored.</param>
/// <returns>Length of compressed data written into the output buffer.</returns>
public static unsafe int Compress(byte[] input, int offset, int length, byte[] output, int outOffset)
{
if (input == null || output == null)
throw new ArgumentNullException();
if (offset < 0 || length < 0 || offset + length > input.Length)
throw new ArgumentOutOfRangeException("Selected range is outside the bounds of the input array");
if (outOffset < 0 || outOffset >= output.Length)
throw new ArgumentOutOfRangeException("Output offset is outside the bounds of the output array");
int outLength = output.Length - outOffset;
if (offset == input.Length)
{
input = new byte[1];
offset = 0;
}
fixed (byte *inputPtr = &input[offset])
fixed (byte* outputPtr = &output[outOffset])
{
var status = NativeProxy.Instance.Compress(inputPtr, length, outputPtr, ref outLength);
if (status == SnappyStatus.Ok)
return outLength;
else if (status == SnappyStatus.BufferTooSmall)
throw new ArgumentOutOfRangeException("Output array is too small");
else
throw new InvalidDataException("Invalid input");
}
}
/// <summary>
/// Compresses byte buffer using Snappy compression.
/// </summary>
/// <param name="input">Input buffer containing data to be compressed. Zero-length input is allowed.</param>
/// <returns>Compressed data.</returns>
public static byte[] Compress(byte[] input)
{
if (input == null)
throw new ArgumentNullException();
var max = GetMaxCompressedLength(input.Length);
var output = new byte[max];
int outLength = Compress(input, 0, input.Length, output, 0);
if (outLength == max)
return output;
var truncated = new byte[outLength];
Array.Copy(output, truncated, outLength);
return truncated;
}
/// <summary>
/// Decompresses data previously compressed with Snappy.
/// </summary>
/// <param name="input">Input buffer containing compressed data.</param>
/// <param name="offset">Offset of compressed data within the input buffer.</param>
/// <param name="length">Length of compressed data within the input buffer.</param>
/// <param name="output">
/// Output buffer where decompressed data will be written.
/// Buffer length minus outOffset must be equal or higher than return value of GetUncompressedLength method.
/// </param>
/// <param name="outOffset">Offset into the output buffer where decompressed data will be stored.</param>
/// <returns>Length of decompressed data written into the output buffer.</returns>
public static unsafe int Uncompress(byte[] input, int offset, int length, byte[] output, int outOffset)
{
if (input == null || output == null)
throw new ArgumentNullException();
if (offset < 0 || length < 0 || offset + length > input.Length)
throw new ArgumentOutOfRangeException("Selected range is outside the bounds of the input array");
if (length == 0)
throw new InvalidDataException("Compressed block cannot be empty");
if (outOffset < 0 || outOffset > output.Length)
throw new ArgumentOutOfRangeException("Output offset is outside the bounds of the output array");
int outLength = output.Length - outOffset;
if (outOffset == output.Length)
{
output = new byte[1];
outOffset = 0;
}
fixed (byte* inputPtr = &input[offset])
fixed (byte* outputPtr = &output[outOffset])
{
var status = NativeProxy.Instance.Uncompress(inputPtr, length, outputPtr, ref outLength);
if (status == SnappyStatus.Ok)
return outLength;
else if (status == SnappyStatus.BufferTooSmall)
throw new ArgumentOutOfRangeException("Output array is too small");
else
throw new InvalidDataException("Input is not a valid snappy-compressed block");
}
}
/// <summary>
/// Decompresses data previously compressed with Snappy.
/// </summary>
/// <param name="input">Input buffer containing compressed data.</param>
/// <returns>Decompressed data.</returns>
public static byte[] Uncompress(byte[] input)
{
var max = GetUncompressedLength(input);
var output = new byte[max];
int outLength = Uncompress(input, 0, input.Length, output, 0);
if (outLength == max)
return output;
var truncated = new byte[outLength];
Array.Copy(output, truncated, outLength);
return truncated;
}
/// <summary>
/// Estimates maximum length of compressed data.
/// Note that compressed data may be slightly larger than uncompressed data in some extreme cases of uncompressible data.
/// </summary>
/// <param name="inLength">Length of uncompressed data used as a basis for calculation of maximum length of compressed data.</param>
/// <returns>Maximum length of compressed data given input of length inLength.</returns>
public static int GetMaxCompressedLength(int inLength)
{
return NativeProxy.Instance.GetMaxCompressedLength(inLength);
}
/// <summary>
/// Retrieves length of uncompressed data for given buffer of compressed data. This is O(1) lookup
/// that merely parses first few bytes of the compressed buffer where the length has been recorded during compression.
/// </summary>
/// <param name="input">Input buffer containing compressed data.</param>
/// <param name="offset">Offset where compressed data is located in the input buffer.</param>
/// <param name="length">Length of compressed data in the input buffer.</param>
/// <returns>Exact length of uncompressed data encoded in the input buffer.</returns>
public static unsafe int GetUncompressedLength(byte[] input, int offset, int length)
{
if (input == null)
throw new ArgumentNullException();
if (offset < 0 || length < 0 || offset + length > input.Length)
throw new ArgumentOutOfRangeException("Selected range is outside the bounds of the input array");
if (length == 0)
throw new InvalidDataException("Compressed block cannot be empty");
fixed (byte* inputPtr = &input[offset])
{
int outLength;
var status = NativeProxy.Instance.GetUncompressedLength(inputPtr, length, out outLength);
if (status == SnappyStatus.Ok)
return outLength;
else
throw new InvalidDataException("Input is not a valid snappy-compressed block");
}
}
/// <summary>
/// Retrieves length of uncompressed data for given buffer of compressed data. This is O(1) lookup
/// that merely parses first few bytes of the compressed buffer where the length has been recorded during compression.
/// </summary>
/// <param name="input">Input buffer containing compressed data.</param>
/// <returns>Exact length of uncompressed data encoded in the input buffer.</returns>
public static int GetUncompressedLength(byte[] input)
{
if (input == null)
throw new ArgumentNullException();
return GetUncompressedLength(input, 0, input.Length);
}
/// <summary>
/// Checks integrity of compressed data. This method performs sanity checks that ensure that the buffer can be decompressed.
/// It doesn't check integrity of data. It merely ensures that decompression will succeed with _some_ result.
/// CRC or other data integrity checks can be provided by higher level protocols like the Snappy framing format.
/// </summary>
/// <param name="input">Input buffer containing compressed data.</param>
/// <param name="offset">Offset where compressed data is located in the input buffer.</param>
/// <param name="length">Length of compressed data in the input buffer.</param>
/// <returns>True if the buffer contains valid Snappy compressed block. False otherwise.</returns>
public static unsafe bool Validate(byte[] input, int offset, int length)
{
if (input == null)
throw new ArgumentNullException();
if (offset < 0 || length < 0 || offset + length > input.Length)
throw new ArgumentOutOfRangeException("Selected range is outside the bounds of the input array");
if (length == 0)
return false;
fixed (byte* inputPtr = &input[offset])
return NativeProxy.Instance.ValidateCompressedBuffer(inputPtr, length) == SnappyStatus.Ok;
}
/// <summary>
/// Checks integrity of compressed data. This method performs sanity checks that ensure that the buffer can be decompressed.
/// It doesn't check integrity of data. It merely ensures that decompression will succeed with _some_ result.
/// CRC or other data integrity checks can be provided by higher level protocols like the Snappy framing format.
/// </summary>
/// <param name="input">Input buffer containing compressed data.</param>
/// <returns>True if the buffer contains valid Snappy compressed block. False otherwise.</returns>
public static bool Validate(byte[] input)
{
if (input == null)
throw new ArgumentNullException();
return Validate(input, 0, input.Length);
}
}
}