| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "rrdengine.h" |
| 4 | #include "dbengine-compression.h" |
| 5 | |
| 6 | #ifdef ENABLE_LZ4 |
| 7 | #include <lz4.h> |
| 8 | #endif |
| 9 | |
| 10 | #ifdef ENABLE_ZSTD |
| 11 | #include <zstd.h> |
| 12 | #define DBENGINE_ZSTD_DEFAULT_COMPRESSION_LEVEL 3 |
| 13 | #endif |
| 14 | |
| 15 | uint8_t dbengine_default_compression(void) { |
| 16 | |
| 17 | #ifdef ENABLE_ZSTD |
| 18 | return RRDENG_COMPRESSION_ZSTD; |
| 19 | #endif |
| 20 | |
| 21 | #ifdef ENABLE_LZ4 |
| 22 | return RRDENG_COMPRESSION_LZ4; |
| 23 | #endif |
| 24 | |
| 25 | return RRDENG_COMPRESSION_NONE; |
| 26 | } |
| 27 | |
| 28 | bool dbengine_valid_compression_algorithm(uint8_t algorithm) { |
| 29 | switch(algorithm) { |
| 30 | case RRDENG_COMPRESSION_NONE: |
| 31 | |
| 32 | #ifdef ENABLE_LZ4 |
| 33 | case RRDENG_COMPRESSION_LZ4: |
| 34 | #endif |
| 35 | |
| 36 | #ifdef ENABLE_ZSTD |
| 37 | case RRDENG_COMPRESSION_ZSTD: |
| 38 | #endif |
| 39 | |
| 40 | return true; |
| 41 | |
| 42 | default: |
| 43 | return false; |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | size_t dbengine_max_compressed_size(size_t uncompressed_size, uint8_t algorithm) { |
| 48 | switch(algorithm) { |
| 49 | #ifdef ENABLE_LZ4 |
| 50 | case RRDENG_COMPRESSION_LZ4: |
| 51 | fatal_assert(uncompressed_size < LZ4_MAX_INPUT_SIZE); |
| 52 | return LZ4_compressBound((int)uncompressed_size); |
| 53 | #endif |
| 54 | |
| 55 | #ifdef ENABLE_ZSTD |
| 56 | case RRDENG_COMPRESSION_ZSTD: |
| 57 | return ZSTD_compressBound(uncompressed_size); |
| 58 | #endif |
| 59 | |
| 60 | case RRDENG_COMPRESSION_NONE: |
| 61 | return uncompressed_size; |
| 62 | |
| 63 | default: { |
| 64 | fatal("DBENGINE: unknown compression algorithm %u", algorithm); |
| 65 | //we will never reach this point, but we have warnings from compiler |
| 66 | return 0; |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | size_t dbengine_compress(void *payload, size_t uncompressed_size, uint8_t algorithm) { |
| 72 | // the result should be stored in the payload |
| 73 | // the caller must have called dbengine_max_compressed_size() to make sure the |
| 74 | // payload is big enough to fit the max size needed. |
| 75 | |
| 76 | switch(algorithm) { |
| 77 | #ifdef ENABLE_LZ4 |
| 78 | case RRDENG_COMPRESSION_LZ4: { |
| 79 | size_t max_compressed_size = dbengine_max_compressed_size(uncompressed_size, algorithm); |
| 80 | struct extent_buffer *eb = extent_buffer_get(max_compressed_size); |
| 81 | void *compressed_buf = eb->data; |
| 82 | |
| 83 | size_t compressed_size = |
| 84 | LZ4_compress_default(payload, compressed_buf, (int)uncompressed_size, (int)max_compressed_size); |
| 85 | |
| 86 | if(compressed_size > 0 && compressed_size < uncompressed_size) |
| 87 | memcpy(payload, compressed_buf, compressed_size); |
| 88 | else |
| 89 | compressed_size = 0; |
| 90 | |
| 91 | extent_buffer_release(eb); |
| 92 | return compressed_size; |
| 93 | } |
| 94 | #endif |
| 95 | |
| 96 | #ifdef ENABLE_ZSTD |
| 97 | case RRDENG_COMPRESSION_ZSTD: { |
| 98 | size_t max_compressed_size = dbengine_max_compressed_size(uncompressed_size, algorithm); |
| 99 | struct extent_buffer *eb = extent_buffer_get(max_compressed_size); |
| 100 | void *compressed_buf = eb->data; |
| 101 | |
| 102 | size_t compressed_size = ZSTD_compress(compressed_buf, max_compressed_size, payload, uncompressed_size, |
| 103 | DBENGINE_ZSTD_DEFAULT_COMPRESSION_LEVEL); |
| 104 | |
| 105 | if (ZSTD_isError(compressed_size)) { |
| 106 | internal_fatal(true, "DBENGINE: ZSTD compression error %s", ZSTD_getErrorName(compressed_size)); |
| 107 | compressed_size = 0; |
| 108 | } |
| 109 | |
| 110 | if(compressed_size > 0 && compressed_size < uncompressed_size) |
| 111 | memcpy(payload, compressed_buf, compressed_size); |
| 112 | else |
| 113 | compressed_size = 0; |
| 114 | |
| 115 | extent_buffer_release(eb); |
| 116 | return compressed_size; |
| 117 | } |
| 118 | #endif |
| 119 | |
| 120 | case RRDENG_COMPRESSION_NONE: |
| 121 | return 0; |
| 122 | |
| 123 | default: { |
| 124 | fatal("DBENGINE: unknown compression algorithm %u", algorithm); |
| 125 | //we will never reach this point, but we have warnings from compiler |
| 126 | return 0; |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | size_t dbengine_decompress(void *dst, void *src, size_t dst_size, size_t src_size, uint8_t algorithm) { |
| 132 | switch(algorithm) { |
| 133 | |
| 134 | #ifdef ENABLE_LZ4 |
| 135 | case RRDENG_COMPRESSION_LZ4: { |
| 136 | int rc = LZ4_decompress_safe(src, dst, (int)src_size, (int)dst_size); |
| 137 | if(rc < 0) { |
| 138 | nd_log(NDLS_DAEMON, NDLP_ERR, "DBENGINE: ZSTD decompression error %d", rc); |
| 139 | rc = 0; |
| 140 | } |
| 141 | |
| 142 | return rc; |
| 143 | } |
| 144 | #endif |
| 145 | |
| 146 | #ifdef ENABLE_ZSTD |
| 147 | case RRDENG_COMPRESSION_ZSTD: { |
| 148 | size_t decompressed_size = ZSTD_decompress(dst, dst_size, src, src_size); |
| 149 | |
| 150 | if (ZSTD_isError(decompressed_size)) { |
| 151 | nd_log(NDLS_DAEMON, NDLP_ERR, "DBENGINE: ZSTD decompression error %s", |
| 152 | ZSTD_getErrorName(decompressed_size)); |
| 153 | |
| 154 | decompressed_size = 0; |
| 155 | } |
| 156 | |
| 157 | return decompressed_size; |
| 158 | } |
| 159 | #endif |
| 160 | |
| 161 | case RRDENG_COMPRESSION_NONE: |
| 162 | internal_fatal(true, "DBENGINE: %s() should not be called for uncompressed pages", __FUNCTION__ ); |
| 163 | return 0; |
| 164 | |
| 165 | default: |
| 166 | internal_fatal(true, "DBENGINE: unknown compression algorithm %u", algorithm); |
| 167 | return 0; |
| 168 | } |
| 169 | } |