Introduce XPRESS compresssion on Windows. (#1081)
Comparable with Snappy on comp ratio. Implemented using Windows API, does not require external package. Avaiable since Windows 8 and server 2012. Use -DXPRESS=1 with CMake to enable.main
parent
874c96ac1d
commit
ee221d2de0
@ -0,0 +1,270 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
|
||||||
|
#include "port/win/xpress_win.h" |
||||||
|
#include <Windows.h> |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <memory> |
||||||
|
#include <limits> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
#ifdef XPRESS |
||||||
|
|
||||||
|
#ifdef JEMALLOC |
||||||
|
#include <jemalloc/jemalloc.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
// Put this under ifdef so windows systems w/o this
|
||||||
|
// can still build
|
||||||
|
#include <compressapi.h> |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
namespace port { |
||||||
|
namespace xpress { |
||||||
|
|
||||||
|
// Helpers
|
||||||
|
namespace { |
||||||
|
|
||||||
|
auto CloseCompressorFun = [](void* h) { |
||||||
|
if (NULL != h) { |
||||||
|
::CloseCompressor(reinterpret_cast<COMPRESSOR_HANDLE>(h)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
auto CloseDecompressorFun = [](void* h) { |
||||||
|
if (NULL != h) { |
||||||
|
::CloseDecompressor(reinterpret_cast<DECOMPRESSOR_HANDLE>(h)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#ifdef JEMALLOC |
||||||
|
// Make sure compressors use our jemalloc if redirected
|
||||||
|
PVOID CompressorAlloc(PVOID, SIZE_T size) { |
||||||
|
return je_malloc(size); |
||||||
|
} |
||||||
|
|
||||||
|
VOID CompressorFree(PVOID, PVOID p) { |
||||||
|
if (p != NULL) { |
||||||
|
je_free(p); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
bool Compress(const char* input, size_t length, std::string* output) { |
||||||
|
|
||||||
|
assert(input != nullptr); |
||||||
|
assert(output != nullptr); |
||||||
|
|
||||||
|
if (length == 0) { |
||||||
|
output->clear(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
COMPRESS_ALLOCATION_ROUTINES* allocRoutinesPtr = nullptr; |
||||||
|
|
||||||
|
#ifdef JEMALLOC |
||||||
|
COMPRESS_ALLOCATION_ROUTINES allocationRoutines; |
||||||
|
|
||||||
|
// Init. allocation routines
|
||||||
|
allocationRoutines.Allocate = CompressorAlloc; |
||||||
|
allocationRoutines.Free = CompressorFree; |
||||||
|
allocationRoutines.UserContext = NULL; |
||||||
|
|
||||||
|
allocRoutinesPtr = &allocationRoutines; |
||||||
|
#endif |
||||||
|
|
||||||
|
COMPRESSOR_HANDLE compressor = NULL; |
||||||
|
|
||||||
|
BOOL success = CreateCompressor( |
||||||
|
COMPRESS_ALGORITHM_XPRESS, // Compression Algorithm
|
||||||
|
allocRoutinesPtr, // Optional allocation routine
|
||||||
|
&compressor); // Handle
|
||||||
|
|
||||||
|
if (!success) { |
||||||
|
#ifdef _DEBUG |
||||||
|
std::cerr << "XPRESS: Failed to create Compressor LastError: " << |
||||||
|
GetLastError() << std::endl; |
||||||
|
#endif |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<void, decltype(CloseCompressorFun)> |
||||||
|
compressorGuard(compressor, CloseCompressorFun); |
||||||
|
|
||||||
|
SIZE_T compressedBufferSize = 0; |
||||||
|
|
||||||
|
// Query compressed buffer size.
|
||||||
|
success = ::Compress( |
||||||
|
compressor, // Compressor Handle
|
||||||
|
const_cast<char*>(input), // Input buffer
|
||||||
|
length, // Uncompressed data size
|
||||||
|
NULL, // Compressed Buffer
|
||||||
|
0, // Compressed Buffer size
|
||||||
|
&compressedBufferSize); // Compressed Data size
|
||||||
|
|
||||||
|
if (!success) { |
||||||
|
|
||||||
|
auto lastError = GetLastError(); |
||||||
|
|
||||||
|
if (lastError != ERROR_INSUFFICIENT_BUFFER) { |
||||||
|
#ifdef _DEBUG |
||||||
|
std::cerr << |
||||||
|
"XPRESS: Failed to estimate compressed buffer size LastError " << |
||||||
|
lastError << std::endl; |
||||||
|
#endif |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assert(compressedBufferSize > 0); |
||||||
|
|
||||||
|
std::string result; |
||||||
|
result.resize(compressedBufferSize); |
||||||
|
|
||||||
|
SIZE_T compressedDataSize = 0; |
||||||
|
|
||||||
|
// Compress
|
||||||
|
success = ::Compress( |
||||||
|
compressor, // Compressor Handle
|
||||||
|
const_cast<char*>(input), // Input buffer
|
||||||
|
length, // Uncompressed data size
|
||||||
|
&result[0], // Compressed Buffer
|
||||||
|
compressedBufferSize, // Compressed Buffer size
|
||||||
|
&compressedDataSize); // Compressed Data size
|
||||||
|
|
||||||
|
if (!success) { |
||||||
|
#ifdef _DEBUG |
||||||
|
std::cerr << "XPRESS: Failed to compress LastError " << |
||||||
|
GetLastError() << std::endl; |
||||||
|
#endif |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
result.resize(compressedDataSize); |
||||||
|
output->swap(result); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
char* Decompress(const char* input_data, size_t input_length, |
||||||
|
int* decompress_size) { |
||||||
|
|
||||||
|
assert(input_data != nullptr); |
||||||
|
assert(decompress_size != nullptr); |
||||||
|
|
||||||
|
if (input_length == 0) { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
COMPRESS_ALLOCATION_ROUTINES* allocRoutinesPtr = nullptr; |
||||||
|
|
||||||
|
#ifdef JEMALLOC |
||||||
|
COMPRESS_ALLOCATION_ROUTINES allocationRoutines; |
||||||
|
|
||||||
|
// Init. allocation routines
|
||||||
|
allocationRoutines.Allocate = CompressorAlloc; |
||||||
|
allocationRoutines.Free = CompressorFree; |
||||||
|
allocationRoutines.UserContext = NULL; |
||||||
|
allocRoutinesPtr = &allocationRoutines; |
||||||
|
#endif |
||||||
|
|
||||||
|
DECOMPRESSOR_HANDLE decompressor = NULL; |
||||||
|
|
||||||
|
BOOL success = CreateDecompressor( |
||||||
|
COMPRESS_ALGORITHM_XPRESS, // Compression Algorithm
|
||||||
|
allocRoutinesPtr, // Optional allocation routine
|
||||||
|
&decompressor); // Handle
|
||||||
|
|
||||||
|
|
||||||
|
if (!success) { |
||||||
|
#ifdef _DEBUG |
||||||
|
std::cerr <<
|
||||||
|
"XPRESS: Failed to create Decompressor LastError " << |
||||||
|
GetLastError() << std::endl; |
||||||
|
#endif |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<void, decltype(CloseDecompressorFun)> |
||||||
|
compressorGuard(decompressor, CloseDecompressorFun); |
||||||
|
|
||||||
|
SIZE_T decompressedBufferSize = 0; |
||||||
|
|
||||||
|
success = ::Decompress( |
||||||
|
decompressor, // Compressor Handle
|
||||||
|
const_cast<char*>(input_data), // Compressed data
|
||||||
|
input_length, // Compressed data size
|
||||||
|
NULL, // Buffer set to NULL
|
||||||
|
0, // Buffer size set to 0
|
||||||
|
&decompressedBufferSize); // Decompressed Data size
|
||||||
|
|
||||||
|
if (!success) { |
||||||
|
|
||||||
|
auto lastError = GetLastError(); |
||||||
|
|
||||||
|
if (lastError != ERROR_INSUFFICIENT_BUFFER) { |
||||||
|
#ifdef _DEBUG |
||||||
|
std::cerr <<
|
||||||
|
"XPRESS: Failed to estimate decompressed buffer size LastError " << |
||||||
|
lastError << std::endl; |
||||||
|
#endif |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
assert(decompressedBufferSize > 0); |
||||||
|
|
||||||
|
// On Windows we are limited to a 32-bit int for the
|
||||||
|
// output data size argument
|
||||||
|
// so we hopefully never get here
|
||||||
|
if (decompressedBufferSize > std::numeric_limits<int>::max()) { |
||||||
|
assert(false); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
// The callers are deallocating using delete[]
|
||||||
|
// thus we must allocate with new[]
|
||||||
|
std::unique_ptr<char[]> outputBuffer(new char[decompressedBufferSize]); |
||||||
|
|
||||||
|
SIZE_T decompressedDataSize = 0; |
||||||
|
|
||||||
|
success = ::Decompress( |
||||||
|
decompressor, |
||||||
|
const_cast<char*>(input_data), |
||||||
|
input_length, |
||||||
|
outputBuffer.get(), |
||||||
|
decompressedBufferSize, |
||||||
|
&decompressedDataSize); |
||||||
|
|
||||||
|
if (!success) { |
||||||
|
#ifdef _DEBUG |
||||||
|
std::cerr << |
||||||
|
"XPRESS: Failed to decompress LastError " << |
||||||
|
GetLastError() << std::endl; |
||||||
|
#endif |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
*decompress_size = static_cast<int>(decompressedDataSize); |
||||||
|
|
||||||
|
// Return the raw buffer to the caller supporting the tradition
|
||||||
|
return outputBuffer.release(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
|
@ -0,0 +1,26 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
namespace port { |
||||||
|
namespace xpress { |
||||||
|
|
||||||
|
bool Compress(const char* input, size_t length, std::string* output); |
||||||
|
|
||||||
|
char* Decompress(const char* input_data, size_t input_length, |
||||||
|
int* decompress_size); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,17 @@ |
|||||||
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
// Xpress on Windows is implemeted using Win API
|
||||||
|
#if defined(ROCKSDB_PLATFORM_POSIX) |
||||||
|
#error "Xpress compression not implemented" |
||||||
|
#elif defined(OS_WIN) |
||||||
|
#include "port/win/xpress_win.h" |
||||||
|
#endif |
Loading…
Reference in new issue