Cuberite
A lightweight, fast and extensible game server for Minecraft
StringCompression.cpp
Go to the documentation of this file.
1 
2 // StringCompression.cpp
3 
4 // Implements the wrapping functions for compression and decompression
5 
6 #include "Globals.h"
7 #include "ByteBuffer.h"
8 #include "StringCompression.h"
9 
10 #include <libdeflate.h>
11 
12 
13 
14 
15 
16 std::string_view Compression::Result::GetStringView() const
17 {
18  const auto View = GetView();
19  return { reinterpret_cast<const char *>(View.data()), View.size() };
20 }
21 
22 
23 
24 
25 
27 {
28  // Get a generic std::byte * to what the variant is currently storing:
29  return
30  {
31  std::visit([](const auto & Buffer) -> const std::byte *
32  {
33  using Variant = std::decay_t<decltype(Buffer)>;
34 
35  if constexpr (std::is_same_v<Variant, Compression::Result::Static>)
36  {
37  return Buffer.data();
38  }
39  else
40  {
41  return Buffer.get();
42  }
43  }, Storage), Size
44  };
45 }
46 
47 
48 
49 
50 
52 {
53  m_Handle = libdeflate_alloc_compressor(CompressionFactor);
54 
55  if (m_Handle == nullptr)
56  {
57  throw std::bad_alloc();
58  }
59 }
60 
61 
62 
63 
64 
66 {
67  libdeflate_free_compressor(m_Handle);
68 }
69 
70 
71 
72 
73 
74 template <auto Algorithm>
75 Compression::Result Compression::Compressor::Compress(const void * const Input, const size_t Size)
76 {
77  // First see if the stack buffer has enough space:
78  {
79  Result::Static Buffer;
80  const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Buffer.data(), Buffer.size());
81 
82  if (BytesWrittenOut != 0)
83  {
84  return { Buffer, BytesWrittenOut };
85  }
86  }
87 
88  // No it doesn't. Allocate space on the heap to write the compression result, increasing in powers of 2.
89  // This will either succeed, or except with bad_alloc.
90 
91  auto DynamicCapacity = Result::StaticCapacity * 2;
92  while (true)
93  {
94  auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity);
95  const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Dynamic.get(), DynamicCapacity);
96 
97  if (BytesWrittenOut != 0)
98  {
99  return { std::move(Dynamic), BytesWrittenOut };
100  }
101 
102  DynamicCapacity *= 2;
103  }
104 }
105 
106 
107 
108 
109 
111 {
112  return Compress<&libdeflate_gzip_compress>(Input.data(), Input.size());
113 }
114 
115 
116 
117 
118 
120 {
121  return Compress<&libdeflate_zlib_compress>(Input.data(), Input.size());
122 }
123 
124 
125 
126 
127 
128 Compression::Result Compression::Compressor::CompressZLib(const void * const Input, const size_t Size)
129 {
130  return Compress<&libdeflate_zlib_compress>(Input, Size);
131 }
132 
133 
134 
135 
136 
138 {
139  m_Handle = libdeflate_alloc_decompressor();
140 
141  if (m_Handle == nullptr)
142  {
143  throw std::bad_alloc();
144  }
145 }
146 
147 
148 
149 
150 
152 {
153  libdeflate_free_decompressor(m_Handle);
154 }
155 
156 
157 
158 
159 
161 {
162  return Extract<&libdeflate_gzip_decompress>(Input);
163 }
164 
165 
166 
167 
168 
170 {
171  return Extract<&libdeflate_zlib_decompress>(Input);
172 }
173 
174 
175 
176 
177 
179 {
180  return Extract<&libdeflate_zlib_decompress>(Input, UncompressedSize);
181 }
182 
183 
184 
185 
186 
187 template <auto Algorithm>
189 {
190  // First see if the stack buffer has enough space:
191  {
192  Result::Static Buffer;
193  size_t BytesWrittenOut;
194 
195  switch (Algorithm(m_Handle, Input.data(), Input.size(), Buffer.data(), Buffer.size(), &BytesWrittenOut))
196  {
197  case LIBDEFLATE_SUCCESS: return { Buffer, BytesWrittenOut };
198  case LIBDEFLATE_INSUFFICIENT_SPACE: break;
199  default: throw std::runtime_error("Data extraction failed.");
200  }
201  }
202 
203  // No it doesn't. Allocate space on the heap to write the compression result, increasing in powers of 2.
204 
205  auto DynamicCapacity = Result::StaticCapacity * 2;
206  while (true)
207  {
208  size_t BytesWrittenOut;
209  auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity);
210 
211  switch (Algorithm(m_Handle, Input.data(), Input.size(), Dynamic.get(), DynamicCapacity, &BytesWrittenOut))
212  {
213  case libdeflate_result::LIBDEFLATE_SUCCESS: return { std::move(Dynamic), BytesWrittenOut };
214  case libdeflate_result::LIBDEFLATE_INSUFFICIENT_SPACE:
215  {
216  DynamicCapacity *= 2;
217  continue;
218  }
219  default: throw std::runtime_error("Data extraction failed.");
220  }
221  }
222 }
223 
224 
225 
226 
227 
228 template <auto Algorithm>
230 {
231  // Here we have the expected size after extraction, so directly use a suitable buffer size:
232  if (UncompressedSize <= Result::StaticCapacity)
233  {
234  if (
235  Result::Static Buffer;
236  Algorithm(m_Handle, Input.data(), Input.size(), Buffer.data(), UncompressedSize, nullptr) == libdeflate_result::LIBDEFLATE_SUCCESS
237  )
238  {
239  return { Buffer, UncompressedSize };
240  }
241  }
242  else if (
243  auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(UncompressedSize);
244  Algorithm(m_Handle, Input.data(), Input.size(), Dynamic.get(), UncompressedSize, nullptr) == libdeflate_result::LIBDEFLATE_SUCCESS
245  )
246  {
247  return { std::move(Dynamic), UncompressedSize };
248  }
249 
250  throw std::runtime_error("Data extraction failed.");
251 }
std::basic_string_view< std::byte > ContiguousByteBufferView
Definition: Globals.h:376
std::enable_if_t< std::is_array_v< T > &&(std::extent_v< T >==0), std::unique_ptr< T > > make_unique_for_overwrite(std::size_t a_Size)
Definition: Globals.h:298
Contains the result of a compression or extraction operation.
static constexpr size_t StaticCapacity
ContiguousByteBufferView GetView() const
Returns a view (of type std::byte) of the internal store.
std::string_view GetStringView() const
Returns a view (of type char) of the internal store.
std::array< std::byte, 128 KiB > Static
Result Compress(const void *Input, size_t Size)
Result CompressGZip(ContiguousByteBufferView Input)
Compressor(int CompressionFactor=6)
Creates a new compressor instance with a compression factor [0-12].
Result CompressZLib(ContiguousByteBufferView Input)
Result ExtractZLib(ContiguousByteBufferView Input)
Result ExtractGZip(ContiguousByteBufferView Input)
Result Extract(ContiguousByteBufferView Input)
Extractor()
Creates a new extractor instance.