Skip to content

Commit 2f4de13

Browse files
nordic-mik7nordicjm
authored andcommittedFeb 18, 2025·
nrf_compress: external dictionary interface
This commit introduces additional interface to LZMA decompression. It provides the user with possibility to store decompression dictionary in memory area of their choice by implementing POSIX-like interfaces (open, close, write, read) and possibly limit RAM usage. In lzma.c, simple caching mechanism for dictionary was introduced to limit 'write to dictionary' operations. Further optimizations may be introduced to also limit 'read' operations if performance of accesing external dictionary is not good enough. Ref: NCSDK-31444 Signed-off-by: Michal Kozikowski <michal.kozikowski@nordicsemi.no>
1 parent e22804c commit 2f4de13

File tree

11 files changed

+1091
-74
lines changed

11 files changed

+1091
-74
lines changed
 

‎.checkpatch.conf

+1
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@
5151
--exclude lib/at_parser/generated
5252
--exclude lib/bin/lwm2m_carrier/include
5353
--exclude tests/lib/uicc_lwm2m
54+
--exclude subsys/nrf_compress/lzma

‎include/nrf_compress/implementation.h

+20-9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef NRF_COMPRESS_IMPLEMENTATION_H_
1313
#define NRF_COMPRESS_IMPLEMENTATION_H_
1414

15+
#include "lzma_types.h"
1516
#include <stdint.h>
1617
#include <stdlib.h>
1718
#include <zephyr/kernel.h>
@@ -32,7 +33,8 @@ extern "C" {
3233
* @typedef nrf_compress_init_func_t
3334
* @brief Initialize compression implementation.
3435
*
35-
* @param[in] inst Reserved for future use, must be NULL.
36+
* @param[in] inst Implementation specific initialization context.
37+
* Concrete implementation may cast it to predefined type.
3638
*
3739
* @retval 0 Success.
3840
* @retval -errno Negative errno code on other failure.
@@ -43,7 +45,8 @@ typedef int (*nrf_compress_init_func_t)(void *inst);
4345
* @typedef nrf_compress_deinit_func_t
4446
* @brief De-initialize compression implementation.
4547
*
46-
* @param[in] inst Reserved for future use, must be NULL.
48+
* @param[in] inst Implementation specific initialization context.
49+
* Concrete implementation may cast it to predefined type.
4750
*
4851
* @retval 0 Success.
4952
* @retval -errno Negative errno code on other failure.
@@ -55,7 +58,8 @@ typedef int (*nrf_compress_deinit_func_t)(void *inst);
5558
* @brief Reset compression state function. Used to abort current compression or
5659
* decompression task before starting a new one.
5760
*
58-
* @param[in] inst Reserved for future use, must be NULL.
61+
* @param[in] inst Implementation specific initialization context.
62+
* Concrete implementation may cast it to predefined type.
5963
*
6064
* @retval 0 Success.
6165
* @retval -errno Negative errno code on other failure.
@@ -66,7 +70,8 @@ typedef int (*nrf_compress_reset_func_t)(void *inst);
6670
* @typedef nrf_compress_compress_func_t
6771
* @brief Placeholder function for future use, do not use.
6872
*
69-
* @param[in] inst Reserved for future use, must be NULL.
73+
* @param[in] inst Implementation specific initialization context.
74+
* Concrete implementation may cast it to predefined type.
7075
*
7176
* @retval 0 Success.
7277
* @retval -errno Negative errno code on other failure.
@@ -80,7 +85,8 @@ typedef int (*nrf_compress_compress_func_t)(void *inst);
8085
* be provided if more is not available (for example, end of data or data is
8186
* is being streamed).
8287
*
83-
* @param[in] inst Reserved for future use, must be NULL.
88+
* @param[in] inst Implementation specific initialization context.
89+
* Concrete implementation may cast it to predefined type.
8490
*
8591
* @retval Positive value Success indicating chunk size.
8692
* @retval -errno Negative errno code on other failure.
@@ -92,7 +98,8 @@ typedef size_t (*nrf_compress_decompress_bytes_needed_t)(void *inst);
9298
* be called one or more times with compressed data to decompress it
9399
* into its natural form.
94100
*
95-
* @param[in] inst Reserved for future use, must be NULL.
101+
* @param[in] inst Implementation specific initialization context.
102+
* Concrete implementation may cast it to predefined type.
96103
* @param[in] input Input data buffer, containing the compressed data.
97104
* @param[in] input_size Size of the input data buffer.
98105
* @param[in] last_part Last part of compressed data. This should be set to true if this is
@@ -103,9 +110,13 @@ typedef size_t (*nrf_compress_decompress_bytes_needed_t)(void *inst);
103110
* offset the input data buffer by this amount of bytes.
104111
* @param[out] output Output data buffer pointer to pointer. This will be set to the
105112
* compression's output buffer when decompressed data is available to
106-
* be used or copied.
107-
* @param[out] output_size Size of data in output data buffer pointer. Data should only be
108-
* read when the value in this pointer is greater than 0.
113+
* be used or copied. WARNING: For LZMA implementation, it is only
114+
* valid if no external dictionary is used. For external dictionary
115+
* variant (indicated by *inst parameter in init function), this
116+
* will be set to NULL and user should read from ones own dictionary.
117+
* @param[out] output_size Size of data in output data buffer pointer (or in external
118+
* dictionary). Data should only be read when the value in this pointer
119+
* is greater than 0.
109120
*
110121
* @retval 0 Success.
111122
* @retval -errno Negative errno code on other failure.

‎include/nrf_compress/lzma_types.h

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
/**
8+
* @file
9+
* @brief LZMA API types for compression/decompression subsystem
10+
*/
11+
12+
#ifndef NRF_COMPRESS_LZMA_TYPES_H_
13+
#define NRF_COMPRESS_LZMA_TYPES_H_
14+
15+
#include <zephyr/types.h>
16+
17+
#ifdef __cplusplus
18+
extern "C" {
19+
#endif
20+
21+
/**
22+
* @typedef lzma_dictionary_open_func_t
23+
* @brief Open dictionary interface. It is up to the user
24+
* how big is the dictionary buffer, but it must be
25+
* at least @a dict_size long.
26+
*
27+
* @param[in] dict_size dictionary size; minimum length of the
28+
* buffer that is requested.
29+
* @param[out] buff_size size of the dictionary buffer provided.
30+
*
31+
* @retval 0 Success.
32+
* @retval -ENOMEM if cannot provide requested size. @a buff_size is valid.
33+
* @retval -errno Negative errno code on other failure @a buff_size is invalid.
34+
*/
35+
typedef int (*lzma_dictionary_open_func_t)(size_t dict_size, size_t *buff_size);
36+
37+
/**
38+
* @typedef lzma_dictionary_close_func_t
39+
* @brief Close dictionary interface.
40+
*
41+
* @retval 0 Success.
42+
* @retval -errno Negative errno code on other failure.
43+
*/
44+
typedef int (*lzma_dictionary_close_func_t)(void);
45+
46+
/**
47+
* @typedef lzma_dictionary_write_func_t
48+
* @brief Write dictionary interface.
49+
*
50+
* @param[in] pos Position (byte-wise) of the dictionary to start writing to.
51+
* @param[in] data Data to write.
52+
* @param[in] len Length of @a data buffer.
53+
*
54+
* @retval Number of written bytes to the dictionary (length).
55+
*/
56+
typedef size_t (*lzma_dictionary_write_func_t)(size_t pos, const uint8_t *data, size_t len);
57+
58+
/**
59+
* @typedef lzma_dictionary_read_func_t
60+
* @brief Read dictionary interface.
61+
*
62+
* @param[in] pos Position of the dictionary to start reading from.
63+
* @param[in] data Data buffer to read into.
64+
* @param[in] len Length of @a data buffer, number of bytes to read.
65+
*
66+
* @retval Number of bytes read from the dictionary (length).
67+
*/
68+
typedef size_t (*lzma_dictionary_read_func_t)(size_t pos, uint8_t *data, size_t len);
69+
70+
typedef struct lzma_dictionary_interface_t {
71+
const lzma_dictionary_open_func_t open;
72+
const lzma_dictionary_close_func_t close;
73+
const lzma_dictionary_write_func_t write;
74+
const lzma_dictionary_read_func_t read;
75+
} lzma_dictionary_interface;
76+
77+
/**
78+
* @brief This is an initialization context struct type. Instantionize and pass it to
79+
* interface functions like for e.g. nrf_compress_init_func_t, nrf_compress_decompress_func_t.
80+
*/
81+
typedef struct lzma_codec_t {
82+
const lzma_dictionary_interface dict_if;
83+
} lzma_codec;
84+
85+
#ifdef __cplusplus
86+
}
87+
#endif
88+
89+
#endif /* NRF_COMPRESS_IMPLEMENTATION_H_ */

‎scripts/ci/license_allow_list.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ none: |
3636
# Ignore all detected licenses in that file.
3737
any: |
3838
.*/license-texts.yaml$
39+
^nrf/subsys/nrf_compress/lzma/
3940
4041
# Allow different licenses from external sources
4142
LicenseRef-west-ncs-sbom-iperf-BSD-3-Clause: "^nrf/ext/"

‎subsys/nrf_compress/Kconfig

+14
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ config NRF_COMPRESS_MEMORY_TYPE_MALLOC
9696

9797
endchoice
9898

99+
config NRF_COMPRESS_EXTERNAL_DICTIONARY
100+
bool "External dictionary"
101+
help
102+
Use external dictionary API for LZMA decompression. It provides the user with
103+
possibility to store dictionary data in memory areas of their choice (e.g. MRAM).
104+
105+
config NRF_COMPRESS_DICTIONARY_CACHE_SIZE
106+
int "Dictionary cache size"
107+
default 1024
108+
depends on NRF_COMPRESS_EXTERNAL_DICTIONARY
109+
help
110+
Cache for last written dictionary data. It limits the number of external dictionary API calls:
111+
'write' and (possibly but not optimized for) 'read'.
112+
99113
config NRF_COMPRESS_MEMORY_ALIGNMENT
100114
int "Buffer memory alignment"
101115
default 4

‎subsys/nrf_compress/lzma/Lzma2Dec.c

+15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* Lzma2Dec.c -- LZMA2 Decoder
22
2024-03-01 : Igor Pavlov : Public domain */
33

4+
/** With changes by Nordic Semiconductor ASA */
5+
46
/* #define SHOW_DEBUG_INFO */
57

68
#include "Precomp.h"
@@ -165,7 +167,15 @@ static unsigned Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b)
165167

166168
static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size)
167169
{
170+
#ifdef CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY
171+
if (LzmaDictionaryWrite(p->dicHandle, p->dicPos, src, size) != size) {
172+
p->state = LZMA2_STATE_ERROR;
173+
return;
174+
}
175+
#else
168176
memcpy(p->dic + p->dicPos, src, size);
177+
#endif
178+
169179
p->dicPos += size;
170180
if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) {
171181
p->checkDicSize = p->prop.dicSize;
@@ -240,6 +250,9 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, const Byte *src, SizeT *
240250
}
241251

242252
LzmaDec_UpdateWithUncompressed(&p->decoder, src, inCur);
253+
if (p->state == LZMA2_STATE_ERROR) {
254+
continue;
255+
}
243256

244257
src += inCur;
245258
*srcLen += inCur;
@@ -419,6 +432,7 @@ ELzma2ParseStatus Lzma2Dec_Parse(CLzma2Dec *p, SizeT outSize, const Byte *src, S
419432
return (ELzma2ParseStatus)LZMA_STATUS_NOT_SPECIFIED;
420433
}
421434

435+
#ifndef CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY
422436
SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
423437
ELzmaFinishMode finishMode, ELzmaStatus *status)
424438
{
@@ -481,5 +495,6 @@ SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, Byt
481495
Lzma2Dec_FreeProbs(&p, alloc);
482496
return res;
483497
}
498+
#endif
484499

485500
#undef PRF

‎subsys/nrf_compress/lzma/LzmaDec.c

+201-5
Large diffs are not rendered by default.

‎subsys/nrf_compress/lzma/LzmaDec.h

+86
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* LzmaDec.h -- LZMA Decoder
22
2023-04-02 : Igor Pavlov : Public domain */
33

4+
/** With changes by Nordic Semiconductor ASA */
5+
46
#ifndef ZIP7_INC_LZMA_DEC_H
57
#define ZIP7_INC_LZMA_DEC_H
68

@@ -47,13 +49,29 @@ SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
4749

4850
#define LZMA_REQUIRED_INPUT_MAX 20
4951

52+
#ifdef CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY
53+
/* Handler type for external dictionary operations.
54+
* See LzmaDictionaryOpen() and other dictionary interfaces to be implemented
55+
* by the user of this library below.
56+
*/
57+
typedef struct DictHandle_t {
58+
BoolInt isOpened;
59+
SizeT dicBufSize;
60+
61+
} DictHandle;
62+
#endif
63+
5064
typedef struct {
5165
/* Don't change this structure. ASM code can use it. */
5266
CLzmaProps prop;
5367
CLzmaProb *probs;
5468
CLzmaProb *probs_1664;
69+
#ifdef CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY
70+
DictHandle *dicHandle;
71+
#else
5572
Byte *dic;
5673
SizeT dicBufSize;
74+
#endif
5775
SizeT dicPos;
5876
const Byte *buf;
5977
UInt32 range;
@@ -69,11 +87,19 @@ typedef struct {
6987
Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
7088
} CLzmaDec;
7189

90+
#ifdef CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY
91+
#define LzmaDec_CONSTRUCT(p) \
92+
{ \
93+
(p)->dicHandle = NULL; \
94+
(p)->probs = NULL; \
95+
}
96+
#else
7297
#define LzmaDec_CONSTRUCT(p) \
7398
{ \
7499
(p)->dic = NULL; \
75100
(p)->probs = NULL; \
76101
}
102+
#endif
77103
#define LzmaDec_Construct(p) LzmaDec_CONSTRUCT(p)
78104

79105
void LzmaDec_Init(CLzmaDec *p);
@@ -229,6 +255,66 @@ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, cons
229255
unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status,
230256
ISzAllocPtr alloc);
231257

258+
259+
#ifdef CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY
260+
/* Interfaces to be implemented by the user of this library.
261+
They are added to allow for different memory storage of dictionary,
262+
not only as directly referenced memory through CLzmaDec::dic.
263+
*/
264+
265+
/* Open dictionary with requested size.
266+
267+
minSize:
268+
Minimal size of the dictionary.
269+
Returns:
270+
Pointer to a dictionary handle struct for subsequent API calls
271+
or NULL if failed to open.
272+
*/
273+
DictHandle *LzmaDictionaryOpen(SizeT minSize);
274+
275+
/* Write to dictionary.
276+
277+
handle:
278+
Pointer to dictionary handle struct, returned from LzmaDictionaryOpen().
279+
pos:
280+
Position (byte-wise) in dictionary to start writing to.
281+
data
282+
Data to be written.
283+
len
284+
Length of @a data.
285+
Returns:
286+
Number of written bytes.
287+
*/
288+
SizeT LzmaDictionaryWrite(DictHandle *handle, SizeT pos, const Byte *data, SizeT len);
289+
290+
/* Read from dictionary.
291+
292+
handle:
293+
Pointer to dictionary handle struct, returned from LzmaDictionaryOpen().
294+
pos:
295+
Position (byte-wise) in dictionary to start reading from.
296+
data
297+
Data buffer to fill in with read data.
298+
len
299+
Length of @a data; number of bytes to be read.
300+
Returns:
301+
Number of read bytes.
302+
*/
303+
SizeT LzmaDictionaryRead(DictHandle *handle, SizeT pos, Byte *data, SizeT len);
304+
305+
/* Close dictionary.
306+
307+
handle:
308+
Pointer to dictionary handle struct, returned from LzmaDictionaryOpen().
309+
310+
Returns:
311+
SZ_OK
312+
SZ_ERROR_PARAM - Invalid input parameters
313+
SZ_ERROR_FAIL - Some unexpected error: internal error of code, memory corruption or hardware
314+
*/
315+
SRes LzmaDictionaryClose(DictHandle *handle);
316+
#endif
317+
232318
EXTERN_C_END
233319

234320
#endif

‎subsys/nrf_compress/src/lzma.c

+424-13
Large diffs are not rendered by default.

‎tests/subsys/nrf_compress/decompression/lzma/src/main.c

+237-47
Large diffs are not rendered by default.

‎tests/subsys/nrf_compress/decompression/lzma/testcase.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ tests:
2323
- CONFIG_NRF_COMPRESS_MEMORY_TYPE_MALLOC=y
2424
- CONFIG_COMMON_LIBC_MALLOC=y
2525
- CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=162000
26+
nrf_compress.decompression.lzma.external_dict:
27+
extra_configs:
28+
- CONFIG_NRF_COMPRESS_EXTERNAL_DICTIONARY=y

0 commit comments

Comments
 (0)
Please sign in to comment.