#include <linux/errno.h>
#include <linux/printk.h>
#include <linux/string.h>
#include <linux/unaligned.h>
#include <linux/zstd.h>
#include "qat_comp_zstd_utils.h"
#define ML_BITS 4
#define ML_MASK ((1U << ML_BITS) - 1)
#define RUN_BITS (8 - ML_BITS)
#define RUN_MASK ((1U << RUN_BITS) - 1)
#define LZ4S_MINMATCH 2
#define QAT_ZSTD_BLOCK_MAX ZSTD_BLOCKSIZE_MAX
static int emit_delimiter(ZSTD_Sequence *out_seqs, size_t *seqs_idx,
size_t out_seqs_capacity, unsigned int lz4s_buff_size)
{
if (*seqs_idx >= out_seqs_capacity - 1) {
pr_debug("QAT ZSTD: sequence overflow (seqs_idx:%zu, capacity:%zu, lz4s_size:%u)\n",
*seqs_idx, out_seqs_capacity, lz4s_buff_size);
return -EOVERFLOW;
}
out_seqs[*seqs_idx].offset = 0;
out_seqs[*seqs_idx].litLength = 0;
out_seqs[*seqs_idx].matchLength = 0;
(*seqs_idx)++;
return 0;
}
int qat_alg_dec_lz4s(ZSTD_Sequence *out_seqs, size_t out_seqs_capacity,
unsigned char *lz4s_buff, unsigned int lz4s_buff_size,
unsigned char *literals, unsigned int *lit_len)
{
unsigned char *end_ip = lz4s_buff + lz4s_buff_size;
unsigned char *start, *dest, *dest_end;
unsigned int hist_literal_len = 0;
unsigned char *ip = lz4s_buff;
size_t block_decomp_size = 0;
size_t seqs_idx = 0;
int ret;
*lit_len = 0;
if (!lz4s_buff_size)
return 0;
while (ip < end_ip) {
size_t literal_len = 0, match_len = 0;
const unsigned int token = *ip++;
size_t length = 0;
size_t offset = 0;
length = token >> ML_BITS;
if (length == RUN_MASK) {
unsigned int s;
do {
s = *ip++;
length += s;
} while (s == 255);
}
literal_len = length;
start = ip;
dest = literals;
dest_end = literals + length;
do {
memcpy(dest, start, QAT_ZSTD_LIT_COPY_LEN);
dest += QAT_ZSTD_LIT_COPY_LEN;
start += QAT_ZSTD_LIT_COPY_LEN;
} while (dest < dest_end);
literals += length;
*lit_len += length;
ip += length;
if (ip == end_ip) {
literal_len += hist_literal_len;
if (block_decomp_size + literal_len > QAT_ZSTD_BLOCK_MAX) {
ret = emit_delimiter(out_seqs, &seqs_idx,
out_seqs_capacity,
lz4s_buff_size);
if (ret)
return ret;
}
out_seqs[seqs_idx].litLength = literal_len;
out_seqs[seqs_idx].offset = offset;
out_seqs[seqs_idx].matchLength = match_len;
break;
}
offset = get_unaligned_le16(ip);
ip += 2;
length = token & ML_MASK;
if (length == ML_MASK) {
unsigned int s;
do {
s = *ip++;
length += s;
} while (s == 255);
}
if (length != 0) {
length += LZ4S_MINMATCH;
match_len = (unsigned short)length;
literal_len += hist_literal_len;
if (block_decomp_size + literal_len + match_len > QAT_ZSTD_BLOCK_MAX) {
ret = emit_delimiter(out_seqs, &seqs_idx,
out_seqs_capacity,
lz4s_buff_size);
if (ret)
return ret;
block_decomp_size = 0;
}
out_seqs[seqs_idx].offset = offset;
out_seqs[seqs_idx].litLength = literal_len;
out_seqs[seqs_idx].matchLength = match_len;
hist_literal_len = 0;
seqs_idx++;
if (seqs_idx >= out_seqs_capacity - 1) {
pr_debug("QAT ZSTD: sequence overflow (seqs_idx:%zu, capacity:%zu, lz4s_size:%u)\n",
seqs_idx, out_seqs_capacity, lz4s_buff_size);
return -EOVERFLOW;
}
block_decomp_size += literal_len + match_len;
} else {
if (literal_len > 0) {
hist_literal_len += literal_len;
}
}
}
return seqs_idx + 1;
}