1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 
10 #include "port/win/xpress_win.h"
11 #include <windows.h>
12 
13 #include <cassert>
14 #include <memory>
15 #include <limits>
16 #include <iostream>
17 
18 #ifdef XPRESS
19 
20 // Put this under ifdef so windows systems w/o this
21 // can still build
22 #include <compressapi.h>
23 
24 namespace ROCKSDB_NAMESPACE {
25 namespace port {
26 namespace xpress {
27 
28 // Helpers
29 namespace {
30 
__anonc93dc2330202(void* h) 31 auto CloseCompressorFun = [](void* h) {
32   if (NULL != h) {
33     ::CloseCompressor(reinterpret_cast<COMPRESSOR_HANDLE>(h));
34   }
35 };
36 
__anonc93dc2330302(void* h) 37 auto CloseDecompressorFun = [](void* h) {
38   if (NULL != h) {
39     ::CloseDecompressor(reinterpret_cast<DECOMPRESSOR_HANDLE>(h));
40   }
41 };
42 }
43 
Compress(const char * input,size_t length,std::string * output)44 bool Compress(const char* input, size_t length, std::string* output) {
45 
46   assert(input != nullptr);
47   assert(output != nullptr);
48 
49   if (length == 0) {
50     output->clear();
51     return true;
52   }
53 
54   COMPRESS_ALLOCATION_ROUTINES* allocRoutinesPtr = nullptr;
55 
56   COMPRESSOR_HANDLE compressor = NULL;
57 
58   BOOL success = CreateCompressor(
59     COMPRESS_ALGORITHM_XPRESS, //  Compression Algorithm
60     allocRoutinesPtr,       //  Optional allocation routine
61     &compressor);              //  Handle
62 
63   if (!success) {
64 #ifdef _DEBUG
65     std::cerr << "XPRESS: Failed to create Compressor LastError: " <<
66       GetLastError() << std::endl;
67 #endif
68     return false;
69   }
70 
71   std::unique_ptr<void, decltype(CloseCompressorFun)>
72     compressorGuard(compressor, CloseCompressorFun);
73 
74   SIZE_T compressedBufferSize = 0;
75 
76   //  Query compressed buffer size.
77   success = ::Compress(
78     compressor,                 //  Compressor Handle
79     const_cast<char*>(input),   //  Input buffer
80     length,                     //  Uncompressed data size
81     NULL,                       //  Compressed Buffer
82     0,                          //  Compressed Buffer size
83     &compressedBufferSize);     //  Compressed Data size
84 
85   if (!success) {
86 
87     auto lastError = GetLastError();
88 
89     if (lastError != ERROR_INSUFFICIENT_BUFFER) {
90 #ifdef _DEBUG
91       std::cerr <<
92         "XPRESS: Failed to estimate compressed buffer size LastError " <<
93         lastError << std::endl;
94 #endif
95       return false;
96     }
97   }
98 
99   assert(compressedBufferSize > 0);
100 
101   std::string result;
102   result.resize(compressedBufferSize);
103 
104   SIZE_T compressedDataSize = 0;
105 
106   //  Compress
107   success = ::Compress(
108     compressor,                  //  Compressor Handle
109     const_cast<char*>(input),    //  Input buffer
110     length,                      //  Uncompressed data size
111     &result[0],                  //  Compressed Buffer
112     compressedBufferSize,        //  Compressed Buffer size
113     &compressedDataSize);        //  Compressed Data size
114 
115   if (!success) {
116 #ifdef _DEBUG
117     std::cerr << "XPRESS: Failed to compress LastError " <<
118       GetLastError() << std::endl;
119 #endif
120     return false;
121   }
122 
123   result.resize(compressedDataSize);
124   output->swap(result);
125 
126   return true;
127 }
128 
Decompress(const char * input_data,size_t input_length,int * decompress_size)129 char* Decompress(const char* input_data, size_t input_length,
130   int* decompress_size) {
131 
132   assert(input_data != nullptr);
133   assert(decompress_size != nullptr);
134 
135   if (input_length == 0) {
136     return nullptr;
137   }
138 
139   COMPRESS_ALLOCATION_ROUTINES* allocRoutinesPtr = nullptr;
140 
141   DECOMPRESSOR_HANDLE decompressor = NULL;
142 
143   BOOL success = CreateDecompressor(
144     COMPRESS_ALGORITHM_XPRESS, //  Compression Algorithm
145     allocRoutinesPtr,          //  Optional allocation routine
146     &decompressor);            //  Handle
147 
148 
149   if (!success) {
150 #ifdef _DEBUG
151     std::cerr << "XPRESS: Failed to create Decompressor LastError "
152       << GetLastError() << std::endl;
153 #endif
154     return nullptr;
155   }
156 
157   std::unique_ptr<void, decltype(CloseDecompressorFun)>
158     compressorGuard(decompressor, CloseDecompressorFun);
159 
160   SIZE_T decompressedBufferSize = 0;
161 
162   success = ::Decompress(
163     decompressor,          //  Compressor Handle
164     const_cast<char*>(input_data),  //  Compressed data
165     input_length,               //  Compressed data size
166     NULL,                        //  Buffer set to NULL
167     0,                           //  Buffer size set to 0
168     &decompressedBufferSize);    //  Decompressed Data size
169 
170   if (!success) {
171 
172     auto lastError = GetLastError();
173 
174     if (lastError != ERROR_INSUFFICIENT_BUFFER) {
175 #ifdef _DEBUG
176       std::cerr
177         << "XPRESS: Failed to estimate decompressed buffer size LastError "
178         << lastError << std::endl;
179 #endif
180       return nullptr;
181     }
182   }
183 
184   assert(decompressedBufferSize > 0);
185 
186   // On Windows we are limited to a 32-bit int for the
187   // output data size argument
188   // so we hopefully never get here
189   if (decompressedBufferSize > std::numeric_limits<int>::max()) {
190     assert(false);
191     return nullptr;
192   }
193 
194   // The callers are deallocating using delete[]
195   // thus we must allocate with new[]
196   std::unique_ptr<char[]> outputBuffer(new char[decompressedBufferSize]);
197 
198   SIZE_T decompressedDataSize = 0;
199 
200   success = ::Decompress(
201     decompressor,
202     const_cast<char*>(input_data),
203     input_length,
204     outputBuffer.get(),
205     decompressedBufferSize,
206     &decompressedDataSize);
207 
208   if (!success) {
209 #ifdef _DEBUG
210     std::cerr <<
211       "XPRESS: Failed to decompress LastError " <<
212       GetLastError() << std::endl;
213 #endif
214     return nullptr;
215   }
216 
217   *decompress_size = static_cast<int>(decompressedDataSize);
218 
219   // Return the raw buffer to the caller supporting the tradition
220   return outputBuffer.release();
221 }
222 }
223 }
224 }  // namespace ROCKSDB_NAMESPACE
225 
226 #endif
227