damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

flatcc_emitter.h (7416B)


      1 #ifndef FLATCC_EMITTER_H
      2 #define FLATCC_EMITTER_H
      3 
      4 #ifdef __cplusplus
      5 extern "C" {
      6 #endif
      7 
      8 /*
      9  * Default implementation of a flatbuilder emitter.
     10  *
     11  * This may be used as a starting point for more advanced emitters,
     12  * for example writing completed pages to disk or network and
     13  * the recycling those pages.
     14  */
     15 
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 #include "flatcc_types.h"
     20 #include "flatcc_iov.h"
     21 #include "flatcc_alloc.h"
     22 
     23 /*
     24  * The buffer steadily grows during emission but the design allows for
     25  * an extension where individual pages can recycled before the buffer
     26  * is complete, for example because they have been transmitted.
     27  *
     28  * When done, the buffer can be cleared to free all memory, or reset to
     29  * maintain an adaptive page pool for next buffer construction.
     30  *
     31  * Unlike an exponentially growing buffer, each buffer page remains
     32  * stable in memory until reset, clear or recycle is called.
     33  *
     34  * Design notes for possible extensions:
     35  *
     36  * The buffer is a ring buffer marked by a front and a back page. The
     37  * front and back may be the same page and may initially be absent.
     38  * Anything outside these pages are unallocated pages for recycling.
     39  * Any page between (but excluding) the front and back pages may be
     40  * recycled by unlinking and relinking outside the front and back pages
     41  * but then copy operations no longer makes sense. Each page stores the
     42  * logical offset within the buffer but isn't otherwise used by the
     43  * implemention - it might be used for network transmission.  The buffer
     44  * is not explicitly designed for multithreaded access but any page
     45  * strictly between front and back is not touched unless recycled and in
     46  * this case aligned allocation is useful to prevent cache line sharing.
     47  */
     48 
     49 /*
     50  * Memory is allocated in fixed length page units - the first page is
     51  * split between front and back so each get half the page size. If the
     52  * size is a multiple of 128 then each page offset will be a multiple of
     53  * 64, which may be useful for sequencing etc.
     54  */
     55 #ifndef FLATCC_EMITTER_PAGE_SIZE
     56 #define FLATCC_EMITTER_MAX_PAGE_SIZE 3000
     57 #define FLATCC_EMITTER_PAGE_MULTIPLE 64
     58 #define FLATCC_EMITTER_PAGE_SIZE ((FLATCC_EMITTER_MAX_PAGE_SIZE) &\
     59     ~(2 * (FLATCC_EMITTER_PAGE_MULTIPLE) - 1))
     60 #endif
     61 
     62 #ifndef FLATCC_EMITTER_ALLOC
     63 #ifdef FLATCC_EMITTER_USE_ALIGNED_ALLOC
     64 /*
     65  * <stdlib.h> does not always provide aligned_alloc, so include whatever
     66  * is required when enabling this feature.
     67  */
     68 #define FLATCC_EMITTER_ALLOC(n) aligned_alloc(FLATCC_EMITTER_PAGE_MULTIPLE,\
     69         (((n) + FLATCC_EMITTER_PAGE_MULTIPLE - 1) & ~(FLATCC_EMITTER_PAGE_MULTIPLE - 1)))
     70 #ifndef FLATCC_EMITTER_FREE
     71 #define FLATCC_EMITTER_FREE(p) aligned_free(p)
     72 #endif
     73 #endif
     74 #endif
     75 
     76 #ifndef FLATCC_EMITTER_ALLOC
     77 #define FLATCC_EMITTER_ALLOC(n) FLATCC_ALLOC(n)
     78 #endif
     79 #ifndef FLATCC_EMITTER_FREE
     80 #define FLATCC_EMITTER_FREE(p) FLATCC_FREE(p)
     81 #endif
     82 
     83 typedef struct flatcc_emitter_page flatcc_emitter_page_t;
     84 typedef struct flatcc_emitter flatcc_emitter_t;
     85 
     86 struct flatcc_emitter_page {
     87     uint8_t page[FLATCC_EMITTER_PAGE_SIZE];
     88     flatcc_emitter_page_t *next;
     89     flatcc_emitter_page_t *prev;
     90     /*
     91      * The offset is relative to page start, but not necessarily
     92      * to any present content if part of front or back page,
     93      * and undefined for unused pages.
     94      */
     95     flatbuffers_soffset_t page_offset;
     96 };
     97 
     98 /*
     99  * Must be allocated and zeroed externally, e.g. on the stack
    100  * then provided as emit_context to the flatbuilder along
    101  * with the `flatcc_emitter` function.
    102  */
    103 struct flatcc_emitter {
    104     flatcc_emitter_page_t *front, *back;
    105     uint8_t *front_cursor;
    106     size_t front_left;
    107     uint8_t *back_cursor;
    108     size_t back_left;
    109     size_t used;
    110     size_t capacity;
    111     size_t used_average;
    112 };
    113 
    114 /* Optional helper to ensure emitter is zeroed initially. */
    115 static inline void flatcc_emitter_init(flatcc_emitter_t *E)
    116 {
    117     memset(E, 0, sizeof(*E));
    118 }
    119 
    120 /* Deallocates all buffer memory making the emitter ready for next use. */
    121 void flatcc_emitter_clear(flatcc_emitter_t *E);
    122 
    123 /*
    124  * Similar to `clear_flatcc_emitter` but heuristacally keeps some allocated
    125  * memory between uses while gradually reducing peak allocations.
    126  * For small buffers, a single page will remain available with no
    127  * additional allocations or deallocations after first use.
    128  */
    129 void flatcc_emitter_reset(flatcc_emitter_t *E);
    130 
    131 /*
    132  * Helper function that allows a page between front and back to be
    133  * recycled while the buffer is still being constructed - most likely as part
    134  * of partial copy or transmission. Attempting to recycle front or back
    135  * pages will result in an error. Recycling pages outside the
    136  * front and back will be valid but pointless. After recycling and copy
    137  * operations are no longer well-defined and should be replaced with
    138  * whatever logic is recycling the pages.  The reset operation
    139  * automatically recycles all (remaining) pages when emission is
    140  * complete. After recycling, the `flatcc_emitter_size` function will
    141  * return as if recycle was not called, but will only represent the
    142  * logical size, not the size of the active buffer. Because a recycled
    143  * page is fully utilized, it is fairly easy to compensate for this if
    144  * required.
    145  *
    146  * Returns 0 on success.
    147  */
    148 int flatcc_emitter_recycle_page(flatcc_emitter_t *E, flatcc_emitter_page_t *p);
    149 
    150 /*
    151  * The amount of data copied with `flatcc_emitter_copy_buffer` and related
    152  * functions. Normally called at end of buffer construction but is
    153  * always valid, as is the copy functions. The size is a direct
    154  * function of the amount emitted data so the flatbuilder itself can
    155  * also provide this information.
    156  */
    157 static inline size_t flatcc_emitter_get_buffer_size(flatcc_emitter_t *E)
    158 {
    159     return E->used;
    160 }
    161 
    162 /*
    163  * Returns buffer start iff the buffer fits on a single internal page.
    164  * Only useful for fairly small buffers - about half the page size since
    165  * one half of first page goes to vtables that likely use little space.
    166  * Returns null if request could not be served.
    167  *
    168  * If `size_out` is not null, it is set to the buffer size, or 0 if
    169  * operation failed.
    170  */
    171 static inline void *flatcc_emitter_get_direct_buffer(flatcc_emitter_t *E, size_t *size_out)
    172 {
    173     if (E->front == E->back) {
    174         if (size_out) {
    175             *size_out = E->used;
    176         }
    177         return E->front_cursor;
    178     }
    179     if (size_out) {
    180         *size_out = 0;
    181     }
    182     return 0;
    183 }
    184 
    185 /*
    186  * Copies the internal flatcc_emitter representation to an externally
    187  * provided linear buffer that must have size `flatcc_emitter_get_size`.
    188  *
    189  * If pages have been recycled, only the remaining pages will be copied
    190  * and thus less data than what `flatcc_emitter_get_size` would suggest. It
    191  * makes more sense to provide a customized copy operation when
    192  * recycling pages.
    193  *
    194  * If the buffer is too small, nothing is copied, otherwise the
    195  * full buffer is copied and the input buffer is returned.
    196  */
    197 void *flatcc_emitter_copy_buffer(flatcc_emitter_t *E, void *buf, size_t size);
    198 
    199 /*
    200  * The emitter interface function to the flatbuilder API.
    201  * `emit_context` should be of type `flatcc_emitter_t` for this
    202  * particular implementation.
    203  *
    204  * This function is compatible with the `flatbuilder_emit_fun`
    205  * type defined in "flatbuilder.h".
    206  */
    207 int flatcc_emitter(void *emit_context,
    208         const flatcc_iovec_t *iov, int iov_count,
    209         flatbuffers_soffset_t offset, size_t len);
    210 
    211 #ifdef __cplusplus
    212 }
    213 #endif
    214 
    215 #endif /* FLATCC_EMITTER_H */