2025-10-28 15:19:30 +00:00
|
|
|
#include <blue/compress/zstd.h>
|
2025-07-28 22:27:24 +01:00
|
|
|
#include <blue/core/ringbuffer.h>
|
|
|
|
|
#include <zstd.h>
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
/*** PRIVATE DATA *************************************************************/
|
|
|
|
|
|
|
|
|
|
struct b_zstd_compressor_p {
|
2025-07-28 22:27:24 +01:00
|
|
|
union {
|
|
|
|
|
ZSTD_CCtx *zstd_c;
|
|
|
|
|
ZSTD_DCtx *zstd_d;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
/*** PUBLIC FUNCTIONS *********************************************************/
|
|
|
|
|
|
|
|
|
|
b_status b_zstd_compressor_get_buffer_size(
|
|
|
|
|
b_compressor_mode mode, size_t *inbuf_size, size_t *outbuf_size)
|
2025-07-28 22:27:24 +01:00
|
|
|
{
|
|
|
|
|
switch (mode) {
|
2025-10-28 15:19:30 +00:00
|
|
|
case B_COMPRESSOR_MODE_COMPRESS:
|
2025-07-28 22:27:24 +01:00
|
|
|
*inbuf_size = ZSTD_CStreamInSize();
|
|
|
|
|
*outbuf_size = ZSTD_CStreamOutSize();
|
|
|
|
|
break;
|
2025-10-28 15:19:30 +00:00
|
|
|
case B_COMPRESSOR_MODE_DECOMPRESS:
|
2025-07-28 22:27:24 +01:00
|
|
|
*inbuf_size = ZSTD_DStreamInSize();
|
|
|
|
|
*outbuf_size = ZSTD_DStreamOutSize();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
/*** VIRTUAL FUNCTIONS ********************************************************/
|
2025-07-28 22:27:24 +01:00
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static void zstd_compressor_init(b_object *obj, void *priv)
|
|
|
|
|
{
|
2025-07-28 22:27:24 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static void zstd_compressor_fini(b_object *obj, void *priv)
|
2025-07-28 22:27:24 +01:00
|
|
|
{
|
2025-10-28 15:19:30 +00:00
|
|
|
b_compressor_data *c = b_object_get_protected(obj, B_TYPE_COMPRESSOR);
|
|
|
|
|
struct b_zstd_compressor_p *ctx = priv;
|
|
|
|
|
switch (c->c_mode) {
|
|
|
|
|
case B_COMPRESSOR_MODE_COMPRESS:
|
2025-07-28 22:27:24 +01:00
|
|
|
ZSTD_freeCCtx(ctx->zstd_c);
|
|
|
|
|
break;
|
2025-10-28 15:19:30 +00:00
|
|
|
case B_COMPRESSOR_MODE_DECOMPRESS:
|
2025-07-28 22:27:24 +01:00
|
|
|
ZSTD_freeDCtx(ctx->zstd_d);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2025-10-28 15:19:30 +00:00
|
|
|
break;
|
2025-07-28 22:27:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static enum b_status reset(b_compressor *compressor)
|
2025-07-28 22:27:24 +01:00
|
|
|
{
|
2025-10-28 15:19:30 +00:00
|
|
|
struct b_zstd_compressor_p *ctx
|
|
|
|
|
= b_object_get_private(compressor, B_TYPE_ZSTD_COMPRESSOR);
|
|
|
|
|
b_compressor_data *data
|
|
|
|
|
= b_object_get_protected(compressor, B_TYPE_COMPRESSOR);
|
|
|
|
|
|
|
|
|
|
if (!ctx || !data) {
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (data->c_mode) {
|
|
|
|
|
case B_COMPRESSOR_MODE_COMPRESS:
|
2025-07-28 22:27:24 +01:00
|
|
|
ZSTD_CCtx_reset(ctx->zstd_c, ZSTD_reset_session_only);
|
|
|
|
|
break;
|
2025-10-28 15:19:30 +00:00
|
|
|
case B_COMPRESSOR_MODE_DECOMPRESS:
|
2025-07-28 22:27:24 +01:00
|
|
|
ZSTD_DCtx_reset(ctx->zstd_d, ZSTD_reset_session_only);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return B_ERR_BAD_STATE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static enum b_status compress(b_compressor *compressor)
|
2025-07-28 22:27:24 +01:00
|
|
|
{
|
|
|
|
|
enum b_status status = B_SUCCESS;
|
2025-10-28 15:19:30 +00:00
|
|
|
struct b_zstd_compressor_p *ctx
|
|
|
|
|
= b_object_get_private(compressor, B_TYPE_ZSTD_COMPRESSOR);
|
|
|
|
|
b_compressor_data *data
|
|
|
|
|
= b_object_get_protected(compressor, B_TYPE_COMPRESSOR);
|
|
|
|
|
|
|
|
|
|
if (!ctx || !data) {
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_ringbuffer *in = data->c_in;
|
|
|
|
|
b_ringbuffer *out = data->c_out;
|
2025-07-28 22:27:24 +01:00
|
|
|
|
|
|
|
|
if (b_ringbuffer_available_data_remaining(in) == 0) {
|
|
|
|
|
return B_ERR_NO_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (b_ringbuffer_write_capacity_remaining(out) == 0) {
|
|
|
|
|
return B_ERR_NO_SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t nr_consumed = 0;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
size_t in_available = 0, out_capacity = 0;
|
2025-07-30 18:31:54 +01:00
|
|
|
const void *in_buf = NULL;
|
|
|
|
|
void *out_buf = NULL;
|
2025-07-28 22:27:24 +01:00
|
|
|
|
|
|
|
|
status = b_ringbuffer_open_read_buffer(in, &in_buf, &in_available);
|
|
|
|
|
if (!B_OK(status)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = b_ringbuffer_open_write_buffer(
|
|
|
|
|
out, &out_buf, &out_capacity);
|
|
|
|
|
if (!B_OK(status)) {
|
|
|
|
|
b_ringbuffer_close_read_buffer(in, &in_buf, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZSTD_inBuffer z_in = {
|
|
|
|
|
.src = in_buf,
|
|
|
|
|
.pos = 0,
|
|
|
|
|
.size = in_available,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ZSTD_outBuffer z_out = {
|
|
|
|
|
.dst = out_buf,
|
|
|
|
|
.pos = 0,
|
|
|
|
|
.size = out_capacity,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
size_t ret = ZSTD_compressStream2(
|
|
|
|
|
ctx->zstd_c, &z_out, &z_in, ZSTD_e_continue);
|
|
|
|
|
if (ZSTD_isError(ret)) {
|
|
|
|
|
status = B_ERR_COMPRESSION_FAILURE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (z_in.pos < z_in.size && z_out.pos < z_out.size);
|
|
|
|
|
|
|
|
|
|
nr_consumed += z_in.pos;
|
|
|
|
|
|
|
|
|
|
b_ringbuffer_close_read_buffer(in, &in_buf, z_in.pos);
|
|
|
|
|
b_ringbuffer_close_write_buffer(out, &out_buf, z_out.pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status == B_ERR_NO_SPACE || status == B_ERR_NO_DATA)
|
|
|
|
|
&& nr_consumed > 0) {
|
|
|
|
|
status = B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static enum b_status compress_end(b_compressor *compressor)
|
2025-07-28 22:27:24 +01:00
|
|
|
{
|
|
|
|
|
enum b_status status = B_SUCCESS;
|
2025-10-28 15:19:30 +00:00
|
|
|
struct b_zstd_compressor_p *ctx
|
|
|
|
|
= b_object_get_private(compressor, B_TYPE_ZSTD_COMPRESSOR);
|
|
|
|
|
b_compressor_data *data
|
|
|
|
|
= b_object_get_protected(compressor, B_TYPE_COMPRESSOR);
|
2025-07-28 22:27:24 +01:00
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
if (!ctx || !data) {
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_ringbuffer *out = data->c_out;
|
2025-07-28 22:27:24 +01:00
|
|
|
if (b_ringbuffer_write_capacity_remaining(out) == 0) {
|
|
|
|
|
return B_ERR_NO_SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool finished = false;
|
|
|
|
|
do {
|
|
|
|
|
void *out_buf = NULL;
|
|
|
|
|
size_t out_capacity = 0;
|
|
|
|
|
status = b_ringbuffer_open_write_buffer(
|
|
|
|
|
out, &out_buf, &out_capacity);
|
|
|
|
|
if (!B_OK(status)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZSTD_inBuffer z_in = {0};
|
|
|
|
|
ZSTD_outBuffer z_out = {
|
|
|
|
|
.dst = out_buf,
|
|
|
|
|
.pos = 0,
|
|
|
|
|
.size = out_capacity,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
size_t ret = ZSTD_compressStream2(
|
|
|
|
|
ctx->zstd_c, &z_out, &z_in, ZSTD_e_end);
|
|
|
|
|
if (ZSTD_isError(ret)) {
|
|
|
|
|
status = B_ERR_COMPRESSION_FAILURE;
|
|
|
|
|
finished = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
2025-10-28 15:19:30 +00:00
|
|
|
data->c_flags |= B_COMPRESSOR_EOF;
|
2025-07-28 22:27:24 +01:00
|
|
|
finished = true;
|
|
|
|
|
}
|
|
|
|
|
} while (!finished && z_out.pos < z_out.size);
|
|
|
|
|
|
|
|
|
|
b_ringbuffer_close_write_buffer(out, &out_buf, z_out.pos);
|
|
|
|
|
} while (!finished);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static enum b_status decompress(b_compressor *compressor)
|
2025-07-28 22:27:24 +01:00
|
|
|
{
|
|
|
|
|
enum b_status status = B_SUCCESS;
|
2025-10-28 15:19:30 +00:00
|
|
|
struct b_zstd_compressor_p *ctx
|
|
|
|
|
= b_object_get_private(compressor, B_TYPE_ZSTD_COMPRESSOR);
|
|
|
|
|
b_compressor_data *data
|
|
|
|
|
= b_object_get_protected(compressor, B_TYPE_COMPRESSOR);
|
|
|
|
|
|
|
|
|
|
if (!ctx || !data) {
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_ringbuffer *in = data->c_in;
|
|
|
|
|
b_ringbuffer *out = data->c_out;
|
2025-07-28 22:27:24 +01:00
|
|
|
|
|
|
|
|
if (b_ringbuffer_available_data_remaining(in) == 0) {
|
|
|
|
|
return B_ERR_NO_DATA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (b_ringbuffer_write_capacity_remaining(out) == 0) {
|
|
|
|
|
return B_ERR_NO_SPACE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t nr_consumed = 0;
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
while (!(data->c_flags & B_COMPRESSOR_EOF)) {
|
2025-07-28 22:27:24 +01:00
|
|
|
size_t in_available = 0, out_capacity = 0;
|
2025-07-30 18:31:54 +01:00
|
|
|
const void *in_buf = NULL;
|
|
|
|
|
void *out_buf = NULL;
|
2025-07-28 22:27:24 +01:00
|
|
|
|
|
|
|
|
status = b_ringbuffer_open_read_buffer(in, &in_buf, &in_available);
|
|
|
|
|
if (!B_OK(status)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = b_ringbuffer_open_write_buffer(
|
|
|
|
|
out, &out_buf, &out_capacity);
|
|
|
|
|
if (!B_OK(status)) {
|
|
|
|
|
b_ringbuffer_close_read_buffer(in, &in_buf, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZSTD_inBuffer z_in = {
|
|
|
|
|
.src = in_buf,
|
|
|
|
|
.pos = 0,
|
|
|
|
|
.size = in_available,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ZSTD_outBuffer z_out = {
|
|
|
|
|
.dst = out_buf,
|
|
|
|
|
.pos = 0,
|
|
|
|
|
.size = out_capacity,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
size_t ret = ZSTD_decompressStream(
|
|
|
|
|
ctx->zstd_d, &z_out, &z_in);
|
|
|
|
|
if (ZSTD_isError(ret)) {
|
|
|
|
|
status = B_ERR_COMPRESSION_FAILURE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
2025-10-28 15:19:30 +00:00
|
|
|
data->c_flags |= B_COMPRESSOR_EOF;
|
2025-07-28 22:27:24 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (z_in.pos < z_in.size && z_out.pos < z_out.size);
|
|
|
|
|
|
|
|
|
|
nr_consumed += z_in.pos;
|
|
|
|
|
|
|
|
|
|
b_ringbuffer_close_read_buffer(in, &in_buf, z_in.pos);
|
|
|
|
|
b_ringbuffer_close_write_buffer(out, &out_buf, z_out.pos);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-30 18:31:54 +01:00
|
|
|
if ((status == B_ERR_NO_SPACE || status == B_ERR_NO_DATA)
|
2025-07-28 22:27:24 +01:00
|
|
|
&& nr_consumed > 0) {
|
|
|
|
|
status = B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:19:30 +00:00
|
|
|
static enum b_status set_mode(b_compressor *compressor, b_compressor_mode mode)
|
|
|
|
|
{
|
|
|
|
|
struct b_zstd_compressor_p *ctx
|
|
|
|
|
= b_object_get_private(compressor, B_TYPE_ZSTD_COMPRESSOR);
|
|
|
|
|
b_compressor_data *data
|
|
|
|
|
= b_object_get_protected(compressor, B_TYPE_COMPRESSOR);
|
|
|
|
|
|
|
|
|
|
if (!ctx || !data) {
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode == data->c_mode) {
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (data->c_mode) {
|
|
|
|
|
case B_COMPRESSOR_MODE_COMPRESS:
|
|
|
|
|
ZSTD_freeCCtx(ctx->zstd_c);
|
|
|
|
|
break;
|
|
|
|
|
case B_COMPRESSOR_MODE_DECOMPRESS:
|
|
|
|
|
ZSTD_freeDCtx(ctx->zstd_d);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data->c_mode = mode;
|
|
|
|
|
|
|
|
|
|
switch (data->c_mode) {
|
|
|
|
|
case B_COMPRESSOR_MODE_COMPRESS:
|
|
|
|
|
ctx->zstd_c = ZSTD_createCCtx();
|
|
|
|
|
break;
|
|
|
|
|
case B_COMPRESSOR_MODE_DECOMPRESS:
|
|
|
|
|
ctx->zstd_d = ZSTD_createDCtx();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** CLASS DEFINITION *********************************************************/
|
|
|
|
|
|
|
|
|
|
B_TYPE_CLASS_DEFINITION_BEGIN(b_zstd_compressor)
|
|
|
|
|
B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT)
|
|
|
|
|
B_INTERFACE_ENTRY(to_string) = NULL;
|
|
|
|
|
B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT)
|
|
|
|
|
|
|
|
|
|
B_TYPE_CLASS_INTERFACE_BEGIN(b_compressor, B_TYPE_COMPRESSOR)
|
|
|
|
|
B_INTERFACE_ENTRY(c_buffer_size)
|
|
|
|
|
= b_zstd_compressor_get_buffer_size;
|
|
|
|
|
B_INTERFACE_ENTRY(c_compress) = compress;
|
|
|
|
|
B_INTERFACE_ENTRY(c_compress_end) = compress_end;
|
|
|
|
|
B_INTERFACE_ENTRY(c_decompress) = decompress;
|
|
|
|
|
B_INTERFACE_ENTRY(c_reset) = reset;
|
|
|
|
|
B_INTERFACE_ENTRY(c_set_mode) = set_mode;
|
|
|
|
|
B_TYPE_CLASS_INTERFACE_END(b_compressor, B_TYPE_COMPRESSOR)
|
|
|
|
|
B_TYPE_CLASS_DEFINITION_END(b_zstd_compressor)
|
|
|
|
|
|
|
|
|
|
B_TYPE_DEFINITION_BEGIN(b_zstd_compressor)
|
|
|
|
|
B_TYPE_ID(0x51d437fc, 0xe789, 0x4105, 0xbac7, 0xe6b3f45df198);
|
|
|
|
|
B_TYPE_EXTENDS(B_TYPE_COMPRESSOR);
|
|
|
|
|
B_TYPE_CLASS(b_zstd_compressor_class);
|
|
|
|
|
B_TYPE_INSTANCE_PRIVATE(struct b_zstd_compressor_p);
|
|
|
|
|
B_TYPE_INSTANCE_INIT(zstd_compressor_init);
|
|
|
|
|
B_TYPE_INSTANCE_FINI(zstd_compressor_fini);
|
|
|
|
|
B_TYPE_DEFINITION_END(b_zstd_compressor)
|