| 1 | /* |
| 2 | * libwebsockets - small server side websockets and web server implementation |
| 3 | * |
| 4 | * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to |
| 8 | * deal in the Software without restriction, including without limitation the |
| 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| 10 | * sell copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 22 | * IN THE SOFTWARE. |
| 23 | */ |
| 24 | |
| 25 | /* |
| 26 | * lws_dsh (Disordered Shared Heap) is an opaque abstraction supporting a single |
| 27 | * linear buffer (overallocated at end of the lws_dsh_t) which may contain |
| 28 | * multiple kinds of packets that are retired out of order, and tracked by kind. |
| 29 | * |
| 30 | * Each kind of packet has an lws_dll2 list of its kind of packets and acts as |
| 31 | * a FIFO; packets of a particular type are always retired in order. But there |
| 32 | * is no requirement about the order types are retired matching the original |
| 33 | * order they arrived. |
| 34 | * |
| 35 | * Gaps are tracked as just another kind of "packet" list. |
| 36 | * |
| 37 | * "allocations" (including gaps) are prepended by an lws_dsh_object_t. |
| 38 | * |
| 39 | * dsh may themselves be on an lws_dll2_owner list, and under memory pressure |
| 40 | * allocate into other buffers on the list. |
| 41 | * |
| 42 | * All management structures exist inside the allocated buffer. |
| 43 | */ |
| 44 | |
| 45 | enum { |
| 46 | LWS_DSHFLAG_ENABLE_COALESCE = (1u << 24), |
| 47 | LWS_DSHFLAG_ENABLE_SPLIT = (1u << 25), |
| 48 | }; |
| 49 | |
| 50 | /** |
| 51 | * lws_dsh_create() - Allocate a DSH buffer |
| 52 | * |
| 53 | * \param owner: the owning list this dsh belongs on, or NULL if standalone |
| 54 | * \param buffer_size: the allocation in bytes |
| 55 | * \param count_kinds: how many separately-tracked fifos use the buffer, or-ed |
| 56 | * with optional LWS_DSHFLAGs |
| 57 | * |
| 58 | * This makes a single heap allocation that includes internal tracking objects |
| 59 | * in the buffer. Sub-allocated objects are bound to a "kind" index and |
| 60 | * managed via a FIFO for each kind. |
| 61 | * |
| 62 | * Every "kind" of allocation shares the same buffer space. |
| 63 | * |
| 64 | * Multiple buffers may be bound together in an lws_dll2 list, and if an |
| 65 | * allocation cannot be satisfied by the local buffer, space can be borrowed |
| 66 | * from other dsh in the same list (the local dsh FIFO tracks these "foreign" |
| 67 | * allocations as if they were local). |
| 68 | * |
| 69 | * Returns an opaque pointer to the dsh, or NULL if allocation failed. |
| 70 | */ |
| 71 | LWS_VISIBLE LWS_EXTERN struct lws_dsh * |
| 72 | lws_dsh_create(lws_dll2_owner_t *owner, size_t buffer_size, int count_kinds); |
| 73 | |
| 74 | LWS_VISIBLE LWS_EXTERN void |
| 75 | lws_dsh_empty(struct lws_dsh *dsh); |
| 76 | |
| 77 | /** |
| 78 | * lws_dsh_destroy() - Destroy a DSH buffer |
| 79 | * |
| 80 | * \param pdsh: pointer to the dsh pointer |
| 81 | * |
| 82 | * Deallocates the DSH and sets *pdsh to NULL. |
| 83 | * |
| 84 | * Before destruction, any foreign buffer usage on the part of this dsh are |
| 85 | * individually freed. All dsh on the same list are walked and checked if they |
| 86 | * have their own foreign allocations on the dsh buffer being destroyed. If so, |
| 87 | * it attempts to migrate the allocation to a dsh that is not currently being |
| 88 | * destroyed. If all else fails (basically the buffer memory is being shrunk) |
| 89 | * unmigratable objects are cleanly destroyed. |
| 90 | */ |
| 91 | LWS_VISIBLE LWS_EXTERN void |
| 92 | lws_dsh_destroy(struct lws_dsh **pdsh); |
| 93 | |
| 94 | /** |
| 95 | * lws_dsh_alloc_tail() - make a suballocation inside a dsh |
| 96 | * |
| 97 | * \param dsh: the dsh tracking the allocation |
| 98 | * \param kind: the kind of allocation |
| 99 | * \param src1: the first source data to copy |
| 100 | * \param size1: the size of the first source data |
| 101 | * \param src2: the second source data to copy (after the first), or NULL |
| 102 | * \param size2: the size of the second source data |
| 103 | * |
| 104 | * Allocates size1 + size2 bytes in a dsh (it prefers the given dsh but will |
| 105 | * borrow space from other dsh on the same list if necessary) and copies size1 |
| 106 | * bytes into it from src1, followed by size2 bytes from src2 if src2 isn't |
| 107 | * NULL. The actual suballocation is a bit larger because of alignment and a |
| 108 | * prepended management header. |
| 109 | * |
| 110 | * The suballocation is added to the kind-specific FIFO at the tail. |
| 111 | */ |
| 112 | LWS_VISIBLE LWS_EXTERN int |
| 113 | lws_dsh_alloc_tail(struct lws_dsh *dsh, int kind, const void *src1, |
| 114 | size_t size1, const void *src2, size_t size2); |
| 115 | |
| 116 | /** |
| 117 | * lws_dsh_free() - free a suballocation from the dsh |
| 118 | * |
| 119 | * \param obj: a pointer to a void * that pointed to the allocated payload |
| 120 | * |
| 121 | * This returns the space used by \p obj in the dsh buffer to the free list |
| 122 | * of the dsh the allocation came from. |
| 123 | */ |
| 124 | LWS_VISIBLE LWS_EXTERN void |
| 125 | lws_dsh_free(void **obj); |
| 126 | |
| 127 | /** |
| 128 | * lws_dsh_consume() - partially consume a dsh |
| 129 | * |
| 130 | * \param dsh: the dsh |
| 131 | * \param kind: the kind of allocation (0 +) |
| 132 | * \param len: length to consume |
| 133 | * |
| 134 | * Consume part of a dsh object. |
| 135 | */ |
| 136 | LWS_VISIBLE LWS_EXTERN void |
| 137 | lws_dsh_consume(struct lws_dsh *dsh, int kind, size_t len); |
| 138 | |
| 139 | LWS_VISIBLE LWS_EXTERN size_t |
| 140 | lws_dsh_get_size(struct lws_dsh *dsh, int kind); |
| 141 | |
| 142 | /** |
| 143 | * lws_dsh_get_head() - get the head allocation inside the dsh |
| 144 | * |
| 145 | * \param dsh: the dsh tracking the allocation |
| 146 | * \param kind: the kind of allocation |
| 147 | * \param obj: pointer to a void * to be set to the payload |
| 148 | * \param size: set to the size of the allocation |
| 149 | * |
| 150 | * This gets the "next" object in the kind FIFO for the dsh, and returns 0 if |
| 151 | * any. If none, returns nonzero. |
| 152 | * |
| 153 | * This is nondestructive of the fifo or the payload. Use lws_dsh_free on |
| 154 | * obj to remove the entry from the kind fifo and return the payload to the |
| 155 | * free list. |
| 156 | */ |
| 157 | LWS_VISIBLE LWS_EXTERN int |
| 158 | lws_dsh_get_head(struct lws_dsh *dsh, int kind, void **obj, size_t *size); |
| 159 | |
| 160 | /** |
| 161 | * lws_dsh_describe() - DEBUG BUILDS ONLY dump the dsh to the logs |
| 162 | * |
| 163 | * \param dsh: the dsh to dump |
| 164 | * \param desc: text that appears at the top of the dump |
| 165 | * |
| 166 | * Useful information for debugging lws_dsh |
| 167 | */ |
| 168 | LWS_VISIBLE LWS_EXTERN void |
| 169 | lws_dsh_describe(struct lws_dsh *dsh, const char *desc); |
| 170 | |