33 communication with HID devices.
44
55 Internal ring buffer — a bounded FIFO of HID input reports.
6- Storage is a single pre-allocated flat byte buffer of
7- (capacity × slot_size) bytes, where slot_size is a backend-specific
8- runtime upper bound on input report size, determined at open time.
6+ Storage is a single pre-allocated allocation owned by r->storage,
7+ holding a lengths array (capacity × sizeof(size_t) bytes) followed
8+ by a slot data region (capacity × slot_size bytes). r->lengths and
9+ r->data are non-owning views into that allocation.
10+ slot_size is a backend-specific runtime upper bound on input
11+ report size, determined at open time.
912 Drop-oldest-when-full semantics.
1013
1114 All helpers are defined as `static`; each translation unit
5558#include <string.h>
5659
5760struct hidapi_input_ring {
58- uint8_t * storage ; /* capacity × slot_size bytes; one allocation */
59- size_t * lengths ; /* actual len of each queued report; capacity entries */
61+ uint8_t * storage ; /* owns the single allocation */
62+ size_t * lengths ; /* non-owning view: lengths region (capacity × sizeof(size_t)) */
63+ uint8_t * data ; /* non-owning view: slot data region (capacity × slot_size) */
6064 int capacity ; /* slot count; changed only by resize() */
6165 size_t slot_size ; /* bytes per slot; fixed at init */
6266 int head ; /* oldest report index (dequeue side) */
@@ -76,19 +80,24 @@ static int hidapi_input_ring_init(struct hidapi_input_ring *r,
7680 return -1 ;
7781 if (r -> storage ) /* double-init guard */
7882 return -1 ;
79- /* Reject capacity × slot_size that would overflow size_t. */
83+ /* Reject overflow on lengths_bytes + data_bytes. */
84+ if ((size_t )capacity > SIZE_MAX / sizeof (size_t ))
85+ return -1 ;
8086 if ((size_t )capacity > SIZE_MAX / slot_size )
8187 return -1 ;
88+ size_t lengths_bytes = (size_t )capacity * sizeof (size_t );
89+ size_t data_bytes = (size_t )capacity * slot_size ;
90+ if (lengths_bytes > SIZE_MAX - data_bytes )
91+ return -1 ;
8292
83- r -> storage = (uint8_t * )malloc ((size_t )capacity * slot_size );
84- r -> lengths = (size_t * )calloc ((size_t )capacity , sizeof (size_t ));
85- if (!r -> storage || !r -> lengths ) {
86- free (r -> storage );
87- free (r -> lengths );
88- r -> storage = NULL ;
93+ r -> storage = (uint8_t * )calloc (1 , lengths_bytes + data_bytes );
94+ if (!r -> storage ) {
8995 r -> lengths = NULL ;
96+ r -> data = NULL ;
9097 return -1 ;
9198 }
99+ r -> lengths = (size_t * )r -> storage ; /* view: lengths region at offset 0 */
100+ r -> data = r -> storage + lengths_bytes ; /* view: data region after lengths */
92101 r -> capacity = capacity ;
93102 r -> slot_size = slot_size ;
94103 r -> head = 0 ;
@@ -99,15 +108,15 @@ static int hidapi_input_ring_init(struct hidapi_input_ring *r,
99108}
100109
101110/* PRECONDITION: caller holds r's mutex, or is tearing down.
102- * Frees storage and lengths ; zeros r. Safe on zero-init or
103- * previously-destroyed rings. */
111+ * Frees the single allocation (owned by r->storage) ; zeros r.
112+ * Safe on zero-init or previously-destroyed rings. */
104113static void hidapi_input_ring_destroy (struct hidapi_input_ring * r )
105114{
106115 if (!r ) return ;
107116 free (r -> storage );
108- free (r -> lengths );
109117 r -> storage = NULL ;
110- r -> lengths = NULL ;
118+ r -> lengths = NULL ; /* non-owning view; null for consistency */
119+ r -> data = NULL ; /* non-owning view; null for consistency */
111120 r -> capacity = 0 ;
112121 r -> slot_size = 0 ;
113122 r -> head = 0 ;
@@ -140,7 +149,7 @@ static int hidapi_input_ring_push(struct hidapi_input_ring *r,
140149 }
141150
142151 if (len > 0 ) {
143- memcpy (r -> storage + (size_t )r -> tail * r -> slot_size , data , len );
152+ memcpy (r -> data + (size_t )r -> tail * r -> slot_size , data , len );
144153 }
145154 r -> lengths [r -> tail ] = len ;
146155 r -> tail = (r -> tail + 1 ) % r -> capacity ;
@@ -182,16 +191,17 @@ static int hidapi_input_ring_pop_into(struct hidapi_input_ring *r,
182191 size_t payload_len = r -> lengths [r -> head ];
183192 size_t to_copy = (payload_len < dst_len ) ? payload_len : dst_len ;
184193 if (to_copy > 0 ) {
185- memcpy (dst , r -> storage + (size_t )r -> head * r -> slot_size , to_copy );
194+ memcpy (dst , r -> data + (size_t )r -> head * r -> slot_size , to_copy );
186195 }
187196 r -> head = (r -> head + 1 ) % r -> capacity ;
188197 r -> count -- ;
189198 return (int )to_copy ;
190199}
191200
192- /* Resize the slot array to new_cap. Allocates a new flat storage buffer
193- * and lengths array, memcpy's surviving reports' bytes into the new
194- * storage preserving FIFO order, then frees the old buffers.
201+ /* Resize the slot array to new_cap. Allocates a new combined block
202+ * (lengths region followed by data region), memcpy's both the surviving
203+ * lengths and their payload bytes into FIFO-ordered positions in the
204+ * new block, then frees the old block.
195205 *
196206 * On shrink below count, the oldest (count - new_cap) reports are
197207 * evicted — matching the push-time drop-oldest policy.
@@ -208,28 +218,33 @@ static int hidapi_input_ring_resize(struct hidapi_input_ring *r, int new_cap)
208218 return -1 ;
209219 if (new_cap == r -> capacity )
210220 return 0 ;
211- /* Reject new_cap × slot_size that would overflow size_t. */
221+ /* Reject overflow on the new combined block size. */
222+ if ((size_t )new_cap > SIZE_MAX / sizeof (size_t ))
223+ return -1 ;
212224 if ((size_t )new_cap > SIZE_MAX / r -> slot_size )
213225 return -1 ;
226+ size_t new_lengths_bytes = (size_t )new_cap * sizeof (size_t );
227+ size_t new_data_bytes = (size_t )new_cap * r -> slot_size ;
228+ if (new_lengths_bytes > SIZE_MAX - new_data_bytes )
229+ return -1 ;
214230
215- uint8_t * new_storage = (uint8_t * )malloc ((size_t )new_cap * r -> slot_size );
216- size_t * new_lengths = (size_t * )calloc ((size_t )new_cap , sizeof (size_t ));
217- if (!new_storage || !new_lengths ) {
218- free (new_storage );
219- free (new_lengths );
231+ uint8_t * new_storage = (uint8_t * )calloc (1 , new_lengths_bytes + new_data_bytes );
232+ if (!new_storage ) {
220233 return -1 ;
221234 }
235+ size_t * new_lengths = (size_t * )new_storage ;
236+ uint8_t * new_data = new_storage + new_lengths_bytes ;
222237
223238 int keep = (r -> count > new_cap ) ? new_cap : r -> count ;
224239 int dropped = r -> count - keep ;
225240
226- /* Copy surviving reports' payload bytes into new storage , packed
241+ /* Copy surviving reports' payload bytes into new data region , packed
227242 * starting at slot 0. Oldest dropped entries are simply not copied. */
228243 int src = (r -> head + dropped ) % r -> capacity ;
229244 for (int dst = 0 ; dst < keep ; dst ++ ) {
230245 if (r -> lengths [src ] > 0 ) {
231- memcpy (new_storage + (size_t )dst * r -> slot_size ,
232- r -> storage + (size_t )src * r -> slot_size ,
246+ memcpy (new_data + (size_t )dst * r -> slot_size ,
247+ r -> data + (size_t )src * r -> slot_size ,
233248 r -> lengths [src ]);
234249 }
235250 new_lengths [dst ] = r -> lengths [src ];
@@ -238,11 +253,11 @@ static int hidapi_input_ring_resize(struct hidapi_input_ring *r, int new_cap)
238253
239254 r -> dropped += dropped ;
240255
241- free (r -> storage );
242- free (r -> lengths );
256+ free (r -> storage ); /* frees the entire old block */
243257
244258 r -> storage = new_storage ;
245259 r -> lengths = new_lengths ;
260+ r -> data = new_data ;
246261 r -> capacity = new_cap ;
247262 r -> head = 0 ;
248263 r -> count = keep ;
0 commit comments