rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
compressed_block_device.cpp
Go to the documentation of this file.
1/**
2 * @file compressed_block_device.cpp
3 * @brief This file implements a ChibiOS block device backed by a compressed (gzip) store.
4 *
5 * @date Mar 4, 2021
6 * @author Matthew Kennedy, (c) 2021
7 *
8 * This works by decompressing one block (512 bytes) at a time.
9 *
10 * For sequential reads, the performance is great - the gzip decompress can only go forwards in the file.
11 * If a block later in the file (but with a gap) is requested, we decompress (and discard) the blocks in the gap,
12 * returning the block requested.
13 *
14 * If a block is requested from before the previous block, we discard decompression state,
15 * reinitialize, and decompress up to that block.
16 *
17 * NOTE: This means performance is terrible for "true" random access! Things go best when you have a few
18 * big files in the filesystem with no fragmentation, so they can be read out in large sequential chunks.
19 *
20 */
21
23
24#include <cstring>
25
26#define BLOCK_SIZE 512
27
28static bool is_inserted(void*) {
29 // Device is always inserted
30 return true;
31}
32
33static bool is_protected(void*) {
34 // Write protected - we can't do random access writes to a compressed volume
35 return true;
36}
37
38static bool connect(void* instance) {
40 if (BLK_STOP == cbd->state) {
41 cbd->state = BLK_READY;
42 }
43 return HAL_SUCCESS;
44}
45
46static bool disconnect(void* instance) {
48 if (BLK_STOP != cbd->state) {
49 cbd->state = BLK_STOP;
50 }
51 return HAL_SUCCESS;
52}
53
54static bool read(void* instance, uint32_t startblk, uint8_t* buffer, uint32_t /*n*/) {
56
57 // If we just initialized, or trying to seek backwards, (re)initialize the decompressor
58 if (cbd->lastBlock == -1 || (int32_t)startblk <= cbd->lastBlock) {
59 uzlib_uncompress_init(&cbd->d, cbd->dictionary, sizeof(cbd->dictionary));
60
61 cbd->d.source = cbd->source;
62 cbd->d.source_limit = cbd->d.source + cbd->sourceSize;
63 cbd->d.source_read_cb = NULL;
64
65 uzlib_gzip_parse_header(&cbd->d);
66
67 cbd->lastBlock = -1;
68 }
69
70 // How many blocks do we need to decompress to get to the one requested?
71 size_t blocks_ahead = startblk - cbd->lastBlock;
72
73 // Decompress blocks until we get to the block we need
74 for (size_t i = 0; i < blocks_ahead; i++) {
75 cbd->d.dest = cbd->d.dest_start = buffer;
76 cbd->d.dest_limit = buffer + BLOCK_SIZE;
77
78 // Decompress one chunk
79 uzlib_uncompress(&cbd->d);
80 }
81
82 // Save the current position in the stream so we can efficiently seek forward later
83 cbd->lastBlock = startblk;
84
85 return HAL_SUCCESS;
86}
87
88static bool write(void*, uint32_t, const uint8_t*, uint32_t) {
89 // you shouldn't be able to do this anyway, so just swallow it, I guess?
90 return HAL_SUCCESS;
91}
92
93constexpr size_t gzSize(const uint8_t* image, size_t imageSize) {
94 // The last 4 bytes of the gzip stream encode the total size in bytes
95 const uint8_t* pSize = image + imageSize - 1;
96 size_t size = *pSize--;
97 size = 256 * size + *pSize--;
98 size = 256 * size + *pSize--;
99 return 256 * size + *pSize--;
100}
101
102static bool get_info(void* instance, BlockDeviceInfo* bdip) {
104 if (cbd->state != BLK_READY) {
105 return HAL_FAILED;
106 }
107
108 // The last 4 bytes of the gzip stream encode the total size in bytes
109 size_t size = gzSize(cbd->source, cbd->sourceSize);
110
111 bdip->blk_num = size / BLOCK_SIZE;
112 bdip->blk_size = BLOCK_SIZE;
113 return HAL_SUCCESS;
114}
115
116static bool sync(void* instance) {
118 if (BLK_READY != cbd->state) {
119 return HAL_FAILED;
120 }
121 else {
122 return HAL_SUCCESS;
123 }
124}
125
126static const BaseBlockDeviceVMT cbdVmt = {
127 (size_t)0, // instanceOffset
130 connect,
132 read,
133 write,
134 sync,
135 get_info,
136};
137
139 cbd->vmt = &cbdVmt;
140 memset(cbd->dictionary, 0, sizeof(cbd->dictionary));
141 cbd->state = BLK_STOP;
142}
143
144void compressedBlockDeviceStart(CompressedBlockDevice* cbd, const uint8_t* source, size_t sourceSize) {
145 cbd->source = source;
146 cbd->sourceSize = sourceSize;
147 cbd->state = BLK_READY;
148 cbd->lastBlock = -1;
149}
static BenchController instance
static bool write(void *, uint32_t, const uint8_t *, uint32_t)
constexpr size_t gzSize(const uint8_t *image, size_t imageSize)
static bool is_inserted(void *)
static const BaseBlockDeviceVMT cbdVmt
static bool disconnect(void *instance)
void compressedBlockDeviceObjectInit(CompressedBlockDevice *cbd)
void compressedBlockDeviceStart(CompressedBlockDevice *cbd, const uint8_t *source, size_t sourceSize)
static bool get_info(void *instance, BlockDeviceInfo *bdip)
static bool connect(void *instance)
static bool read(void *instance, uint32_t startblk, uint8_t *buffer, uint32_t)
static bool is_protected(void *)
static bool sync(void *instance)
This file implements a ChibiOS block device backed by a compressed (gzip) store.
static CompressedBlockDevice cbd
const BaseBlockDeviceVMT * vmt
_base_block_device_data int32_t lastBlock
composite packet size
static BigBufferHandle buffer