rusEFI
The most advanced open source ECU
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 
28 static bool is_inserted(void*) {
29  // Device is always inserted
30  return true;
31 }
32 
33 static bool is_protected(void*) {
34  // Write protected - we can't do random access writes to a compressed volume
35  return true;
36 }
37 
38 static bool connect(void* instance) {
40  if (BLK_STOP == cbd->state) {
41  cbd->state = BLK_READY;
42  }
43  return HAL_SUCCESS;
44 }
45 
46 static bool disconnect(void* instance) {
48  if (BLK_STOP != cbd->state) {
49  cbd->state = BLK_STOP;
50  }
51  return HAL_SUCCESS;
52 }
53 
54 static 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 
88 static 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 
93 constexpr 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 
102 static 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 
116 static bool sync(void* instance) {
118  if (BLK_READY != cbd->state) {
119  return HAL_FAILED;
120  }
121  else {
122  return HAL_SUCCESS;
123  }
124 }
125 
126 static const BaseBlockDeviceVMT cbdVmt = {
127  (size_t)0, // instanceOffset
128  is_inserted,
129  is_protected,
130  connect,
131  disconnect,
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 
144 void 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 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
HIP9011 instance
const BaseBlockDeviceVMT * vmt
_base_block_device_data int32_t lastBlock
composite packet size
static BigBufferHandle buffer