-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add protocol message compression support:
* Peers negotiate compression via HTTP Header "X-Offer-Compression: lz4" * Messages greater than 70 bytes and protocol type messages MANIFESTS, ENDPOINTS, TRANSACTION, GET_LEDGER, LEDGER_DATA, GET_OBJECT, and VALIDATORLIST are compressed * If the compressed message is larger than the uncompressed message then the uncompressed message is sent * Compression flag and the compression algorithm type are included in the message header * Only LZ4 block compression is currently supported
- Loading branch information
1 parent
ade5eb7
commit 758a379
Showing
14 changed files
with
873 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
//------------------------------------------------------------------------------ | ||
/* | ||
This file is part of rippled: https://github.com/ripple/rippled | ||
Copyright (c) 2020 Ripple Labs Inc. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
//============================================================================== | ||
|
||
#ifndef RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED | ||
#define RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED | ||
|
||
#include <ripple/basics/contract.h> | ||
#include <lz4.h> | ||
#include <algorithm> | ||
|
||
namespace ripple { | ||
|
||
namespace compression_algorithms { | ||
|
||
/** Convenience wrapper for Throw | ||
* @param message Message to log/throw | ||
*/ | ||
inline void doThrow(const char *message) | ||
{ | ||
Throw<std::runtime_error>(message); | ||
} | ||
|
||
/** LZ4 block compression. | ||
* @tparam BufferFactory Callable object or lambda. | ||
* Takes the requested buffer size and returns allocated buffer pointer. | ||
* @param in Data to compress | ||
* @param inSize Size of the data | ||
* @param bf Compressed buffer allocator | ||
* @return Size of compressed data, or zero if failed to compress | ||
*/ | ||
template<typename BufferFactory> | ||
std::size_t | ||
lz4Compress(void const* in, | ||
std::size_t inSize, BufferFactory&& bf) | ||
{ | ||
if (inSize > UINT32_MAX) | ||
doThrow("lz4 compress: invalid size"); | ||
|
||
auto const outCapacity = LZ4_compressBound(inSize); | ||
|
||
// Request the caller to allocate and return the buffer to hold compressed data | ||
auto compressed = bf(outCapacity); | ||
|
||
auto compressedSize = LZ4_compress_default( | ||
reinterpret_cast<const char*>(in), | ||
reinterpret_cast<char*>(compressed), | ||
inSize, | ||
outCapacity); | ||
if (compressedSize == 0) | ||
doThrow("lz4 compress: failed"); | ||
|
||
return compressedSize; | ||
} | ||
|
||
/** | ||
* @param in Compressed data | ||
* @param inSize Size of compressed data | ||
* @param decompressed Buffer to hold decompressed data | ||
* @param decompressedSize Size of the decompressed buffer | ||
* @return size of the decompressed data | ||
*/ | ||
inline | ||
std::size_t | ||
lz4Decompress(std::uint8_t const* in, std::size_t inSize, | ||
std::uint8_t* decompressed, std::size_t decompressedSize) | ||
{ | ||
auto ret = LZ4_decompress_safe(reinterpret_cast<const char*>(in), | ||
reinterpret_cast<char*>(decompressed), inSize, decompressedSize); | ||
|
||
if (ret <= 0 || ret != decompressedSize) | ||
doThrow("lz4 decompress: failed"); | ||
|
||
return decompressedSize; | ||
} | ||
|
||
/** LZ4 block decompression. | ||
* @tparam InputStream ZeroCopyInputStream | ||
* @param in Input source stream | ||
* @param inSize Size of compressed data | ||
* @param decompressed Buffer to hold decompressed data | ||
* @param decompressedSize Size of the decompressed buffer | ||
* @return size of the decompressed data | ||
*/ | ||
template<typename InputStream> | ||
std::size_t | ||
lz4Decompress(InputStream& in, std::size_t inSize, | ||
std::uint8_t* decompressed, std::size_t decompressedSize) | ||
{ | ||
std::vector<std::uint8_t> compressed; | ||
std::uint8_t const* chunk = nullptr; | ||
int chunkSize = 0; | ||
int copiedInSize = 0; | ||
auto const currentBytes = in.ByteCount(); | ||
|
||
// Use the first chunk if it is >= inSize bytes of the compressed message. | ||
// Otherwise copy inSize bytes of chunks into compressed buffer and | ||
// use the buffer to decompress. | ||
while (in.Next(reinterpret_cast<void const**>(&chunk), &chunkSize)) | ||
{ | ||
if (copiedInSize == 0) | ||
{ | ||
if (chunkSize >= inSize) | ||
{ | ||
copiedInSize = inSize; | ||
break; | ||
} | ||
compressed.resize(inSize); | ||
} | ||
|
||
chunkSize = chunkSize < (inSize - copiedInSize) ? chunkSize : (inSize - copiedInSize); | ||
|
||
std::copy(chunk, chunk + chunkSize, compressed.data() + copiedInSize); | ||
|
||
copiedInSize += chunkSize; | ||
|
||
if (copiedInSize == inSize) | ||
{ | ||
chunk = compressed.data(); | ||
break; | ||
} | ||
} | ||
|
||
// Put back unused bytes | ||
if (in.ByteCount() > (currentBytes + copiedInSize)) | ||
in.BackUp(in.ByteCount() - currentBytes - copiedInSize); | ||
|
||
if ((copiedInSize == 0 && chunkSize < inSize) || (copiedInSize > 0 && copiedInSize != inSize)) | ||
doThrow("lz4 decompress: insufficient input size"); | ||
|
||
return lz4Decompress(chunk, inSize, decompressed, decompressedSize); | ||
} | ||
|
||
} // compression | ||
|
||
} // ripple | ||
|
||
#endif //RIPPLED_COMPRESSIONALGORITHMS_H_INCLUDED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
//------------------------------------------------------------------------------ | ||
/* | ||
This file is part of rippled: https://github.com/ripple/rippled | ||
Copyright (c) 2020 Ripple Labs Inc. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
//============================================================================== | ||
|
||
#ifndef RIPPLED_COMPRESSION_H_INCLUDED | ||
#define RIPPLED_COMPRESSION_H_INCLUDED | ||
|
||
#include <ripple/basics/CompressionAlgorithms.h> | ||
#include <ripple/basics/Log.h> | ||
#include <lz4frame.h> | ||
|
||
namespace ripple { | ||
|
||
namespace compression { | ||
|
||
std::size_t constexpr headerBytes = 6; | ||
std::size_t constexpr headerBytesCompressed = 10; | ||
|
||
enum class Algorithm : std::uint8_t { | ||
None = 0x00, | ||
LZ4 = 0x01 | ||
}; | ||
|
||
enum class Compressed : std::uint8_t { | ||
On, | ||
Off | ||
}; | ||
|
||
/** Decompress input stream. | ||
* @tparam InputStream ZeroCopyInputStream | ||
* @param in Input source stream | ||
* @param inSize Size of compressed data | ||
* @param decompressed Buffer to hold decompressed message | ||
* @param algorithm Compression algorithm type | ||
* @return Size of decompressed data or zero if failed to decompress | ||
*/ | ||
template<typename InputStream> | ||
std::size_t | ||
decompress(InputStream& in, std::size_t inSize, std::uint8_t* decompressed, | ||
std::size_t decompressedSize, Algorithm algorithm = Algorithm::LZ4) { | ||
try | ||
{ | ||
if (algorithm == Algorithm::LZ4) | ||
return ripple::compression_algorithms::lz4Decompress(in, inSize, | ||
decompressed, decompressedSize); | ||
else | ||
{ | ||
JLOG(debugLog().warn()) << "decompress: invalid compression algorithm " | ||
<< static_cast<int>(algorithm); | ||
assert(0); | ||
} | ||
} | ||
catch (...) {} | ||
return 0; | ||
} | ||
|
||
/** Compress input data. | ||
* @tparam BufferFactory Callable object or lambda. | ||
* Takes the requested buffer size and returns allocated buffer pointer. | ||
* @param in Data to compress | ||
* @param inSize Size of the data | ||
* @param bf Compressed buffer allocator | ||
* @param algorithm Compression algorithm type | ||
* @return Size of compressed data, or zero if failed to compress | ||
*/ | ||
template<class BufferFactory> | ||
std::size_t | ||
compress(void const* in, | ||
std::size_t inSize, BufferFactory&& bf, Algorithm algorithm = Algorithm::LZ4) { | ||
try | ||
{ | ||
if (algorithm == Algorithm::LZ4) | ||
return ripple::compression_algorithms::lz4Compress(in, inSize, std::forward<BufferFactory>(bf)); | ||
else | ||
{ | ||
JLOG(debugLog().warn()) << "compress: invalid compression algorithm" | ||
<< static_cast<int>(algorithm); | ||
assert(0); | ||
} | ||
} | ||
catch (...) {} | ||
return 0; | ||
} | ||
} // compression | ||
|
||
} // ripple | ||
|
||
#endif //RIPPLED_COMPRESSION_H_INCLUDED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
758a379
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#3287