Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (rbuf.h). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #ifndef __LIBRETRO_SDK_ARRAY_RBUF_H__ | |
24 | #define __LIBRETRO_SDK_ARRAY_RBUF_H__ | |
25 | ||
26 | /* | |
27 | * This file implements stretchy buffers as invented (?) by Sean Barrett. | |
28 | * Based on the implementation from the public domain Bitwise project | |
29 | * by Per Vognsen - https://github.com/pervognsen/bitwise | |
30 | * | |
31 | * It's a super simple type safe dynamic array for C with no need | |
32 | * to predeclare any type or anything. | |
33 | * The first time an element is added, memory for 16 elements are allocated. | |
34 | * Then every time length is about to exceed capacity, capacity is doubled. | |
35 | * | |
36 | * Be careful not to supply modifying statements to the macro arguments. | |
37 | * Something like RBUF_REMOVE(buf, i--); would have unintended results. | |
38 | * | |
39 | * Sample usage: | |
40 | * | |
41 | * mytype_t* buf = NULL; | |
42 | * RBUF_PUSH(buf, some_element); | |
43 | * RBUF_PUSH(buf, other_element); | |
44 | * -- now RBUF_LEN(buf) == 2, buf[0] == some_element, buf[1] == other_element | |
45 | * | |
46 | * -- Free allocated memory: | |
47 | * RBUF_FREE(buf); | |
48 | * -- now buf == NULL, RBUF_LEN(buf) == 0, RBUF_CAP(buf) == 0 | |
49 | * | |
50 | * -- Explicitly increase allocated memory and set capacity: | |
51 | * RBUF_FIT(buf, 100); | |
52 | * -- now RBUF_LEN(buf) == 0, RBUF_CAP(buf) == 100 | |
53 | * | |
54 | * -- Resize buffer (does not initialize or zero memory!) | |
55 | * RBUF_RESIZE(buf, 200); | |
56 | * -- now RBUF_LEN(buf) == 200, RBUF_CAP(buf) == 200 | |
57 | * | |
58 | * -- To handle running out of memory: | |
59 | * bool ran_out_of_memory = !RBUF_TRYFIT(buf, 1000); | |
60 | * -- before RESIZE or PUSH. When out of memory, buf will stay unmodified. | |
61 | */ | |
62 | ||
63 | #include <retro_math.h> /* for MAX */ | |
64 | #include <stdlib.h> /* for malloc, realloc */ | |
65 | ||
66 | #define RBUF__HDR(b) (((struct rbuf__hdr *)(b))-1) | |
67 | ||
68 | #define RBUF_LEN(b) ((b) ? RBUF__HDR(b)->len : 0) | |
69 | #define RBUF_CAP(b) ((b) ? RBUF__HDR(b)->cap : 0) | |
70 | #define RBUF_END(b) ((b) + RBUF_LEN(b)) | |
71 | #define RBUF_SIZEOF(b) ((b) ? RBUF_LEN(b)*sizeof(*b) : 0) | |
72 | ||
73 | #define RBUF_FREE(b) ((b) ? (free(RBUF__HDR(b)), (b) = NULL) : 0) | |
74 | #define RBUF_FIT(b, n) ((size_t)(n) <= RBUF_CAP(b) ? 0 : (*(void**)(&(b)) = rbuf__grow((b), (n), sizeof(*(b))))) | |
75 | #define RBUF_PUSH(b, val) (RBUF_FIT((b), 1 + RBUF_LEN(b)), (b)[RBUF__HDR(b)->len++] = (val)) | |
76 | #define RBUF_POP(b) (b)[--RBUF__HDR(b)->len] | |
77 | #define RBUF_RESIZE(b, sz) (RBUF_FIT((b), (sz)), ((b) ? RBUF__HDR(b)->len = (sz) : 0)) | |
78 | #define RBUF_CLEAR(b) ((b) ? RBUF__HDR(b)->len = 0 : 0) | |
79 | #define RBUF_TRYFIT(b, n) (RBUF_FIT((b), (n)), (((b) && RBUF_CAP(b) >= (size_t)(n)) || !(n))) | |
80 | #define RBUF_REMOVE(b, idx) memmove((b) + (idx), (b) + (idx) + 1, (--RBUF__HDR(b)->len - (idx)) * sizeof(*(b))) | |
81 | ||
82 | struct rbuf__hdr | |
83 | { | |
84 | size_t len; | |
85 | size_t cap; | |
86 | }; | |
87 | ||
88 | #ifdef __GNUC__ | |
89 | __attribute__((__unused__)) | |
90 | #elif defined(_MSC_VER) | |
91 | #pragma warning(push) | |
92 | #pragma warning(disable:4505) //unreferenced local function has been removed | |
93 | #endif | |
94 | static void *rbuf__grow(void *buf, | |
95 | size_t new_len, size_t elem_size) | |
96 | { | |
97 | struct rbuf__hdr *new_hdr; | |
98 | size_t new_cap = MAX(2 * RBUF_CAP(buf), MAX(new_len, 16)); | |
99 | size_t new_size = sizeof(struct rbuf__hdr) + new_cap*elem_size; | |
100 | if (buf) | |
101 | { | |
102 | new_hdr = (struct rbuf__hdr *)realloc(RBUF__HDR(buf), new_size); | |
103 | if (!new_hdr) | |
104 | return buf; /* out of memory, return unchanged */ | |
105 | } | |
106 | else | |
107 | { | |
108 | new_hdr = (struct rbuf__hdr *)malloc(new_size); | |
109 | if (!new_hdr) | |
110 | return NULL; /* out of memory */ | |
111 | new_hdr->len = 0; | |
112 | } | |
113 | new_hdr->cap = new_cap; | |
114 | return new_hdr + 1; | |
115 | } | |
116 | #if defined(_MSC_VER) | |
117 | #pragma warning(pop) | |
118 | #endif | |
119 | ||
120 | #endif |