summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-03-30 02:29:35 +0100
committerAidan MacDonald <amachronic@protonmail.com>2022-09-19 15:09:51 -0400
commitb12427741a66d7af983f0efd85a447cbdd6afbb9 (patch)
tree03d02345339e7f0aa48d6aa1cecc046a708d668e
parentfdde6bb5a7de9d1b013dd8da48d6a603b482f1f6 (diff)
downloadrockbox-b12427741a66d7af983f0efd85a447cbdd6afbb9.tar.gz
rockbox-b12427741a66d7af983f0efd85a447cbdd6afbb9.zip
buflib: clean up and refactor to improve maintainability
Buflib is written with a lot of hardcoded offsets to header fields, arbitrary pointer arithmetic, and similar but not quite duplicated code, making maintenance a nightmare. Most of the pointer arithmetic involving header fields is replaced by indexing from two well-defined pointers, the block start and end pointers. The start pointer points to the first header field, and he end pointer is one past the end of the header. Hardcoded field indices are replaced by two enums. Forward indices (fidx_XXX) are used to access fields from a block start pointer and negated backward indices (-bidx_XXX) are used to index from a block end pointer. There is no overlap between the indices because of the variable length name field in the middle of the header. The length of the fixed fields in the block header is now a #define'd constant rather than being open coded. There is now a function to acquire the block end pointer from the user data pointer (ie. the pointer stored in the handle table). The old code was not consistent in this; some functions would handle a non-aligned user pointer, which may occur as a result of shrinking, while other uses just assumed the user pointer was aligned. Block CRC calculations have also been factored out to a function that accepts block start and end pointers. Change-Id: I6a7e8a8c58aec6c6eaf0e5021400032d8e5f841e
-rw-r--r--firmware/buflib.c238
1 files changed, 133 insertions, 105 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c
index e7835c8a2e..7b1e52eb58 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -97,13 +97,34 @@
97 97
98#define BPANICF panicf 98#define BPANICF panicf
99 99
100/* Forward indices, used to index a block start pointer as block[fidx_XXX] */
101enum {
102 fidx_LEN, /* length of the block, must come first */
103 fidx_HANDLE, /* pointer to entry in the handle table */
104 fidx_OPS, /* pointer to an ops struct */
105 fidx_NAME, /* name, optional and variable length, must come last */
106};
107
108/* Backward indices, used to index a block end pointer as block[-bidx_XXX] */
109enum {
110 bidx_USER, /* dummy to get below fields to be 1-based */
111 bidx_CRC, /* CRC, protects all metadata behind it */
112 bidx_BSIZE, /* total size of the block header */
113};
114
115/* Number of fields in the block header, excluding the name, which is
116 * accounted for using the BSIZE field. Note that bidx_USER is not an
117 * actual field so it is not included in the count. */
118#define BUFLIB_NUM_FIELDS 5
119
100struct buflib_callbacks buflib_ops_locked = { 120struct buflib_callbacks buflib_ops_locked = {
101 .move_callback = NULL, 121 .move_callback = NULL,
102 .shrink_callback = NULL, 122 .shrink_callback = NULL,
103 .sync_callback = NULL, 123 .sync_callback = NULL,
104}; 124};
105 125
106#define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback) 126#define IS_MOVABLE(a) (!a[fidx_OPS].ops || a[fidx_OPS].ops->move_callback)
127
107static union buflib_data* find_first_free(struct buflib_context *ctx); 128static union buflib_data* find_first_free(struct buflib_context *ctx);
108static union buflib_data* find_block_before(struct buflib_context *ctx, 129static union buflib_data* find_block_before(struct buflib_context *ctx,
109 union buflib_data* block, 130 union buflib_data* block,
@@ -241,7 +262,7 @@ union buflib_data* handle_to_block(struct buflib_context* ctx, int handle)
241 if (!data) 262 if (!data)
242 return NULL; 263 return NULL;
243 264
244 return data - data[-2].val; 265 return data - data[-bidx_BSIZE].val;
245} 266}
246 267
247/* Shrink the handle table, returning true if its size was reduced, false if 268/* Shrink the handle table, returning true if its size was reduced, false if
@@ -263,6 +284,20 @@ static inline bool handle_table_shrink(struct buflib_context *ctx)
263 return handle != old_last; 284 return handle != old_last;
264} 285}
265 286
287static inline
288union buflib_data* userpointer_to_block_end(void *userpointer)
289{
290 union buflib_data *data = ALIGN_DOWN(userpointer, sizeof(*data));
291 return data;
292}
293
294static uint32_t calc_block_crc(union buflib_data *block,
295 union buflib_data *block_end)
296{
297 union buflib_data *crc_slot = &block_end[-bidx_CRC];
298 const size_t size = (crc_slot - block) * sizeof(*block);
299 return crc_32(block, size, 0xffffffff);
300}
266 301
267/* If shift is non-zero, it represents the number of places to move 302/* If shift is non-zero, it represents the number of places to move
268 * blocks in memory. Calculate the new address for this block, 303 * blocks in memory. Calculate the new address for this block,
@@ -275,32 +310,29 @@ static bool
275move_block(struct buflib_context* ctx, union buflib_data* block, int shift) 310move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
276{ 311{
277 char* new_start; 312 char* new_start;
313 union buflib_data *new_block;
278 314
279 if (block < ctx->buf_start || block > ctx->alloc_end) 315 if (block < ctx->buf_start || block > ctx->alloc_end)
280 buflib_panic(ctx, "buflib data corrupted %p", block); 316 buflib_panic(ctx, "buflib data corrupted %p", block);
281 317
282 union buflib_data *new_block, *tmp = block[1].handle, *crc_slot; 318 union buflib_data *h_entry = block[fidx_HANDLE].handle;
283 struct buflib_callbacks *ops = block[2].ops; 319 union buflib_data *block_end = userpointer_to_block_end(h_entry->alloc);
284 crc_slot = (union buflib_data*)tmp->alloc - 1;
285 if (crc_slot < ctx->buf_start || crc_slot > ctx->alloc_end)
286 buflib_panic(ctx, "buflib metadata corrupted %p", crc_slot);
287 320
288 const int metadata_size = (crc_slot - block)*sizeof(union buflib_data); 321 uint32_t crc = calc_block_crc(block, block_end);
289 uint32_t crc = crc_32((void *)block, metadata_size, 0xffffffff); 322 if (crc != block_end[-bidx_CRC].crc)
290
291 /* check for metadata validity */
292 if (crc != crc_slot->crc)
293 buflib_panic(ctx, "buflib metadata corrupted, crc: 0x%08x, expected: 0x%08x", 323 buflib_panic(ctx, "buflib metadata corrupted, crc: 0x%08x, expected: 0x%08x",
294 (unsigned int)crc, (unsigned int)crc_slot->crc); 324 (unsigned int)crc, (unsigned int)block_end[-bidx_CRC].crc);
295 325
296 if (!IS_MOVABLE(block)) 326 if (!IS_MOVABLE(block))
297 return false; 327 return false;
298 328
299 int handle = ctx->handle_table - tmp; 329 int handle = ctx->handle_table - h_entry;
300 BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name, 330 BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__,
301 handle, shift, shift*(int)sizeof(union buflib_data)); 331 block[fidx_NAME].name, handle, shift, shift*(int)sizeof(union buflib_data));
302 new_block = block + shift; 332 new_block = block + shift;
303 new_start = tmp->alloc + shift*sizeof(union buflib_data); 333 new_start = h_entry->alloc + shift*sizeof(union buflib_data);
334
335 struct buflib_callbacks *ops = block[fidx_OPS].ops;
304 336
305 /* If move must be synchronized with use, user should have specified a 337 /* If move must be synchronized with use, user should have specified a
306 callback that handles this */ 338 callback that handles this */
@@ -308,10 +340,10 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
308 ops->sync_callback(handle, true); 340 ops->sync_callback(handle, true);
309 341
310 bool retval = false; 342 bool retval = false;
311 if (!ops || ops->move_callback(handle, tmp->alloc, new_start) 343 if (!ops || ops->move_callback(handle, h_entry->alloc, new_start)
312 != BUFLIB_CB_CANNOT_MOVE) 344 != BUFLIB_CB_CANNOT_MOVE)
313 { 345 {
314 tmp->alloc = new_start; /* update handle table */ 346 h_entry->alloc = new_start; /* update handle table */
315 memmove(new_block, block, block->val * sizeof(union buflib_data)); 347 memmove(new_block, block, block->val * sizeof(union buflib_data));
316 retval = true; 348 retval = true;
317 } 349 }
@@ -424,41 +456,50 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
424 this < ctx->alloc_end; 456 this < ctx->alloc_end;
425 before = this, this += abs(this->val)) 457 before = this, this += abs(this->val))
426 { 458 {
427 if (this->val > 0 && this[2].ops 459 if (this->val <= 0)
428 && this[2].ops->shrink_callback) 460 continue;
461
462 struct buflib_callbacks *ops = this[fidx_OPS].ops;
463 if (!ops || !ops->shrink_callback)
464 continue;
465
466 union buflib_data* h_entry = this[fidx_HANDLE].handle;
467 int handle = ctx->handle_table - h_entry;
468
469 unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
470 /* adjust what we ask for if there's free space in the front
471 * this isn't too unlikely assuming this block is
472 * shrinkable but not movable */
473 if (pos_hints == BUFLIB_SHRINK_POS_FRONT &&
474 before != this && before->val < 0)
429 { 475 {
430 int ret; 476 size_t free_space = (-before->val) * sizeof(union buflib_data);
431 int handle = ctx->handle_table - this[1].handle; 477 size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
432 char* data = this[1].handle->alloc; 478 if (wanted < free_space) /* no shrink needed? */
433 bool last = (this+this->val) == ctx->alloc_end; 479 continue;
434 unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK; 480 wanted -= free_space;
435 /* adjust what we ask for if there's free space in the front 481 shrink_hints = pos_hints | wanted;
436 * this isn't too unlikely assuming this block is
437 * shrinkable but not movable */
438 if (pos_hints == BUFLIB_SHRINK_POS_FRONT
439 && before != this && before->val < 0)
440 {
441 size_t free_space = (-before->val) * sizeof(union buflib_data);
442 size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
443 if (wanted < free_space) /* no shrink needed? */
444 continue;
445 wanted -= free_space;
446 shrink_hints = pos_hints | wanted;
447 }
448 ret = this[2].ops->shrink_callback(handle, shrink_hints,
449 data, (char*)(this+this->val)-data);
450 result |= (ret == BUFLIB_CB_OK);
451 /* 'this' might have changed in the callback (if it shrinked
452 * from the top or even freed the handle), get it again */
453 this = handle_to_block(ctx, handle);
454 /* The handle was possibly be freed in the callback,
455 * re-run the loop with the handle before */
456 if (!this)
457 this = before;
458 /* could also change with shrinking from back */
459 else if (last)
460 ctx->alloc_end = this + this->val;
461 } 482 }
483
484 char* data = h_entry->alloc;
485 char* data_end = (char*)(this + this->val);
486 bool last = (data_end == (char*)ctx->alloc_end);
487
488 int ret = ops->shrink_callback(handle, shrink_hints,
489 data, data_end - data);
490 result |= (ret == BUFLIB_CB_OK);
491
492 /* 'this' might have changed in the callback (if it shrinked
493 * from the top or even freed the handle), get it again */
494 this = handle_to_block(ctx, handle);
495
496 /* The handle was possibly be freed in the callback,
497 * re-run the loop with the handle before */
498 if (!this)
499 this = before;
500 /* could also change with shrinking from back */
501 else if (last)
502 ctx->alloc_end = this + this->val;
462 } 503 }
463 /* shrinking was successful at least once, try compaction again */ 504 /* shrinking was successful at least once, try compaction again */
464 if (result) 505 if (result)
@@ -547,9 +588,7 @@ buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
547 size += name_len; 588 size += name_len;
548 size = (size + sizeof(union buflib_data) - 1) / 589 size = (size + sizeof(union buflib_data) - 1) /
549 sizeof(union buflib_data) 590 sizeof(union buflib_data)
550 /* add 5 objects for alloc len, pointer to handle table entry and 591 + BUFLIB_NUM_FIELDS;
551 * name length, the ops pointer and crc */
552 + 5;
553handle_alloc: 592handle_alloc:
554 handle = handle_alloc(ctx); 593 handle = handle_alloc(ctx);
555 if (!handle) 594 if (!handle)
@@ -559,7 +598,7 @@ handle_alloc:
559 */ 598 */
560 union buflib_data* last_block = find_block_before(ctx, 599 union buflib_data* last_block = find_block_before(ctx,
561 ctx->alloc_end, false); 600 ctx->alloc_end, false);
562 struct buflib_callbacks* ops = last_block[2].ops; 601 struct buflib_callbacks* ops = last_block[fidx_OPS].ops;
563 unsigned hints = 0; 602 unsigned hints = 0;
564 if (!ops || !ops->shrink_callback) 603 if (!ops || !ops->shrink_callback)
565 { /* the last one isn't shrinkable 604 { /* the last one isn't shrinkable
@@ -627,23 +666,22 @@ buffer_alloc:
627 /* Set up the allocated block, by marking the size allocated, and storing 666 /* Set up the allocated block, by marking the size allocated, and storing
628 * a pointer to the handle. 667 * a pointer to the handle.
629 */ 668 */
630 union buflib_data *header_size_slot, *crc_slot; 669 block[fidx_LEN].val = size;
631 block->val = size; 670 block[fidx_HANDLE].handle = handle;
632 block[1].handle = handle; 671 block[fidx_OPS].ops = ops;
633 block[2].ops = ops;
634 if (name_len > 0) 672 if (name_len > 0)
635 strcpy(block[3].name, name); 673 strcpy(block[fidx_NAME].name, name);
636 header_size_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len); 674
637 header_size_slot->val = 5 + name_len/sizeof(union buflib_data); 675 size_t bsize = BUFLIB_NUM_FIELDS + name_len/sizeof(union buflib_data);
638 crc_slot = (union buflib_data*)(header_size_slot + 1); 676 union buflib_data *block_end = block + bsize;
639 crc_slot->crc = crc_32((void *)block, 677 block_end[-bidx_BSIZE].val = bsize;
640 (crc_slot - block)*sizeof(union buflib_data), 678 block_end[-bidx_CRC].crc = calc_block_crc(block, block_end);
641 0xffffffff); 679
642 handle->alloc = (char*)(crc_slot + 1); 680 handle->alloc = (char*)&block_end[-bidx_USER];
643 681
644 BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n", 682 BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n",
645 (unsigned int)size, (void *)handle, (void *)ops, 683 (unsigned int)size, (void *)handle, (void *)ops,
646 (unsigned int)crc_slot->crc, name ? block[3].name:""); 684 (unsigned int)block_end[-bidx_CRC].crc, name ? name : "");
647 685
648 block += size; 686 block += size;
649 /* alloc_end must be kept current if we're taking the last block. */ 687 /* alloc_end must be kept current if we're taking the last block. */
@@ -743,7 +781,7 @@ free_space_at_end(struct buflib_context* ctx)
743{ 781{
744 /* subtract 5 elements for 782 /* subtract 5 elements for
745 * val, handle, meta_len, ops and the handle table entry*/ 783 * val, handle, meta_len, ops and the handle table entry*/
746 ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5); 784 ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - BUFLIB_NUM_FIELDS);
747 diff -= 16; /* space for future handles */ 785 diff -= 16; /* space for future handles */
748 diff *= sizeof(union buflib_data); /* make it bytes */ 786 diff *= sizeof(union buflib_data); /* make it bytes */
749 diff -= 16; /* reserve 16 for the name */ 787 diff -= 16; /* reserve 16 for the name */
@@ -858,8 +896,6 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size,
858bool 896bool
859buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) 897buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size)
860{ 898{
861 union buflib_data *crc_slot;
862 int size_for_crc32;
863 char* oldstart = buflib_get_data(ctx, handle); 899 char* oldstart = buflib_get_data(ctx, handle);
864 char* newstart = new_start; 900 char* newstart = new_start;
865 char* newend = newstart + new_size; 901 char* newend = newstart + new_size;
@@ -883,9 +919,9 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
883 metadata_size.val = aligned_oldstart - block; 919 metadata_size.val = aligned_oldstart - block;
884 /* update val and the handle table entry */ 920 /* update val and the handle table entry */
885 new_block = aligned_newstart - metadata_size.val; 921 new_block = aligned_newstart - metadata_size.val;
886 block[0].val = new_next_block - new_block; 922 block[fidx_LEN].val = new_next_block - new_block;
887 923
888 block[1].handle->alloc = newstart; 924 block[fidx_HANDLE].handle->alloc = newstart;
889 if (block != new_block) 925 if (block != new_block)
890 { 926 {
891 /* move metadata over, i.e. pointer to handle table entry and name 927 /* move metadata over, i.e. pointer to handle table entry and name
@@ -906,9 +942,9 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
906 } 942 }
907 943
908 /* update crc of the metadata */ 944 /* update crc of the metadata */
909 crc_slot = (union buflib_data*)new_block[1].handle->alloc - 1; 945 union buflib_data *new_h_entry = new_block[fidx_HANDLE].handle;
910 size_for_crc32 = (crc_slot - new_block)*sizeof(union buflib_data); 946 union buflib_data *new_block_end = userpointer_to_block_end(new_h_entry->alloc);
911 crc_slot->crc = crc_32((void *)new_block, size_for_crc32, 0xffffffff); 947 new_block_end[-bidx_CRC].crc = calc_block_crc(new_block, new_block_end);
912 948
913 /* Now deal with size changes that create free blocks after the allocation */ 949 /* Now deal with size changes that create free blocks after the allocation */
914 if (old_next_block != new_next_block) 950 if (old_next_block != new_next_block)
@@ -932,12 +968,12 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
932const char* buflib_get_name(struct buflib_context *ctx, int handle) 968const char* buflib_get_name(struct buflib_context *ctx, int handle)
933{ 969{
934 union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data)); 970 union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data));
935 size_t len = data[-2].val; 971 size_t len = data[-bidx_BSIZE].val;
936 if (len <= 5) 972 if (len <= BUFLIB_NUM_FIELDS)
937 return NULL; 973 return NULL;
938 974
939 data -= len; 975 data -= len;
940 return data[3].name; 976 return data[fidx_NAME].name;
941} 977}
942 978
943#ifdef DEBUG 979#ifdef DEBUG
@@ -952,10 +988,6 @@ void *buflib_get_data(struct buflib_context *ctx, int handle)
952 988
953void buflib_check_valid(struct buflib_context *ctx) 989void buflib_check_valid(struct buflib_context *ctx)
954{ 990{
955 union buflib_data *crc_slot;
956 int metadata_size;
957 uint32_t crc;
958
959 for(union buflib_data* this = ctx->buf_start; 991 for(union buflib_data* this = ctx->buf_start;
960 this < ctx->alloc_end; 992 this < ctx->alloc_end;
961 this += abs(this->val)) 993 this += abs(this->val))
@@ -963,14 +995,14 @@ void buflib_check_valid(struct buflib_context *ctx)
963 if (this->val < 0) 995 if (this->val < 0)
964 continue; 996 continue;
965 997
966 crc_slot = (union buflib_data*) 998 union buflib_data *h_entry = this[fidx_HANDLE].handle;
967 ((union buflib_data*)this[1].handle)->alloc - 1; 999 union buflib_data *block_end = userpointer_to_block_end(h_entry->alloc);
968 metadata_size = (crc_slot - this)*sizeof(union buflib_data); 1000 uint32_t crc = calc_block_crc(this, block_end);
969 crc = crc_32((void *)this, metadata_size, 0xffffffff); 1001 if (crc != block_end[-bidx_CRC].crc)
970 1002 {
971 if (crc != crc_slot->crc)
972 buflib_panic(ctx, "crc mismatch: 0x%08x, expected: 0x%08x", 1003 buflib_panic(ctx, "crc mismatch: 0x%08x, expected: 0x%08x",
973 (unsigned int)crc, (unsigned int)crc_slot->crc); 1004 (unsigned int)crc, (unsigned int)block_end[-bidx_CRC].crc);
1005 }
974 } 1006 }
975} 1007}
976#endif 1008#endif
@@ -985,16 +1017,11 @@ void buflib_print_allocs(struct buflib_context *ctx,
985 { 1017 {
986 if (!this->alloc) continue; 1018 if (!this->alloc) continue;
987 1019
988 int handle_num; 1020 int handle_num = end - this;
989 const char *name; 1021 void* alloc_start = this->alloc;
990 union buflib_data *block_start, *alloc_start; 1022 union buflib_data *block_start = handle_to_block(ctx, handle_num);
991 intptr_t alloc_len; 1023 const char* name = buflib_get_name(ctx, handle_num);
992 1024 intptr_t alloc_len = block_start[fidx_LEN];
993 handle_num = end - this;
994 alloc_start = buflib_get_data(ctx, handle_num);
995 name = buflib_get_name(ctx, handle_num);
996 block_start = (union buflib_data*)name - 3;
997 alloc_len = block_start->val * sizeof(union buflib_data);
998 1025
999 snprintf(buf, sizeof(buf), 1026 snprintf(buf, sizeof(buf),
1000 "%s(%d):\t%p\n" 1027 "%s(%d):\t%p\n"
@@ -1016,8 +1043,8 @@ void buflib_print_blocks(struct buflib_context *ctx,
1016 this += abs(this->val)) 1043 this += abs(this->val))
1017 { 1044 {
1018 snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)", 1045 snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)",
1019 this, this->val, 1046 this, this->val,
1020 this->val > 0? this[3].name:"<unallocated>"); 1047 this->val > 0 ? this[fidx_NAME].name : "<unallocated>");
1021 print(i++, buf); 1048 print(i++, buf);
1022 } 1049 }
1023} 1050}
@@ -1045,9 +1072,10 @@ void buflib_print_block_at(struct buflib_context *ctx, int block_num,
1045 this += abs(this->val); 1072 this += abs(this->val);
1046 block_num -= 1; 1073 block_num -= 1;
1047 } 1074 }
1075
1048 snprintf(buf, bufsize, "%8p: val: %4ld (%s)", 1076 snprintf(buf, bufsize, "%8p: val: %4ld (%s)",
1049 this, (long)this->val, 1077 this, (long)this->val,
1050 this->val > 0? this[3].name:"<unallocated>"); 1078 this->val > 0 ? this[fidx_NAME].name : "<unallocated>");
1051} 1079}
1052 1080
1053#endif 1081#endif