src/block.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef BLOCK_H | ||
| 2 | #define BLOCK_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include "util/debug.h" | ||
| 6 | #include "util/list.h" | ||
| 7 | #include "util/macros.h" | ||
| 8 | |||
| 9 | // Blocks always contain whole lines. | ||
| 10 | // There's one zero-sized block for an empty file. | ||
| 11 | // Otherwise zero-sized blocks are forbidden. | ||
| 12 | typedef struct { | ||
| 13 | ListHead node; | ||
| 14 | char NONSTRING *data; | ||
| 15 | size_t size; | ||
| 16 | size_t alloc; | ||
| 17 | size_t nl; | ||
| 18 | } Block; | ||
| 19 | |||
| 20 | enum { | ||
| 21 | BLOCK_ALLOC_MULTIPLE = 64, | ||
| 22 | }; | ||
| 23 | |||
| 24 | #define block_for_each(block_, list_head_) \ | ||
| 25 | for ( \ | ||
| 26 | block_ = BLOCK((list_head_)->next); \ | ||
| 27 | &block_->node != (list_head_); \ | ||
| 28 | block_ = block_next(block_) \ | ||
| 29 | ) | ||
| 30 | |||
| 31 | // NOLINTNEXTLINE(readability-identifier-naming) | ||
| 32 | 2861 | static inline Block *BLOCK(ListHead *item) | |
| 33 | { | ||
| 34 | 2861 | static_assert(offsetof(Block, node) == 0); | |
| 35 | 2861 | return (Block*)item; | |
| 36 | } | ||
| 37 | |||
| 38 | NONNULL_ARGS WARN_UNUSED_RESULT | ||
| 39 | 259 | static inline bool block_has_next(const Block *blk, const ListHead *head) | |
| 40 | { | ||
| 41 | 259 | return blk->node.next != head; | |
| 42 | } | ||
| 43 | |||
| 44 | NONNULL_ARGS WARN_UNUSED_RESULT | ||
| 45 | 54 | static inline bool block_has_prev(const Block *blk, const ListHead *head) | |
| 46 | { | ||
| 47 | 54 | return blk->node.prev != head; | |
| 48 | } | ||
| 49 | |||
| 50 | NONNULL_ARGS_AND_RETURN WARN_UNUSED_RESULT | ||
| 51 | 504 | static inline Block *block_next(const Block *blk) | |
| 52 | { | ||
| 53 | 504 | return BLOCK(blk->node.next); | |
| 54 | } | ||
| 55 | |||
| 56 | NONNULL_ARGS_AND_RETURN WARN_UNUSED_RESULT | ||
| 57 | ✗ | static inline Block *block_prev(const Block *blk) | |
| 58 | { | ||
| 59 | ✗ | return BLOCK(blk->node.prev); | |
| 60 | } | ||
| 61 | |||
| 62 | 472 | static inline void block_sanity_check(const Block *blk) | |
| 63 | { | ||
| 64 | 472 | BUG_ON(!blk); | |
| 65 | 472 | BUG_ON(blk->size > blk->alloc); | |
| 66 | 472 | BUG_ON(blk->nl > blk->size); | |
| 67 | |||
| 68 | // block_new() forbids `alloc == 0` and thus always allocates | ||
| 69 | // at least BLOCK_ALLOC_MULTIPLE bytes | ||
| 70 | 472 | BUG_ON(blk->alloc < BLOCK_ALLOC_MULTIPLE); | |
| 71 | 472 | BUG_ON(!blk->data); | |
| 72 | 472 | } | |
| 73 | |||
| 74 | Block *block_new(size_t alloc) RETURNS_NONNULL WARN_UNUSED_RESULT; | ||
| 75 | void block_grow(Block *blk, size_t alloc) NONNULL_ARGS; | ||
| 76 | void block_free(Block *blk) NONNULL_ARGS; | ||
| 77 | |||
| 78 | #endif | ||
| 79 |