1 /*
2 * Copyright (c) 2018 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #ifndef _SER_ATOM_H
19 #define _SER_ATOM_H
20
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <stdatomic.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
32
33 #ifndef SER_ATOM_API
34 # define SER_ATOM_API static
35 #endif
36
37 typedef void *(*ser_atom_realloc_t)(void *data, void *buf, size_t size);
38 typedef void (*ser_atom_free_t)(void *data, void *buf);
39
40 typedef struct _ser_atom_t ser_atom_t;
41
42 SER_ATOM_API int
43 ser_atom_init(ser_atom_t *ser);
44
45 SER_ATOM_API int
46 ser_atom_funcs(ser_atom_t *ser, ser_atom_realloc_t realloc,
47 ser_atom_free_t free, void *data);
48
49 SER_ATOM_API int
50 ser_atom_reset(ser_atom_t *ser, LV2_Atom_Forge *forge);
51
52 SER_ATOM_API LV2_Atom *
53 ser_atom_get(ser_atom_t *ser);
54
55 SER_ATOM_API int
56 ser_atom_deinit(ser_atom_t *ser);
57
58 #ifdef SER_ATOM_IMPLEMENTATION
59
60 struct _ser_atom_t {
61 ser_atom_realloc_t realloc;
62 ser_atom_free_t free;
63 void *data;
64
65 size_t size;
66 size_t offset;
67 union {
68 uint8_t *buf;
69 LV2_Atom *atom;
70 };
71 };
72
73 static LV2_Atom_Forge_Ref
_ser_atom_sink(LV2_Atom_Forge_Sink_Handle handle,const void * buf,uint32_t size)74 _ser_atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void *buf,
75 uint32_t size)
76 {
77 ser_atom_t *ser = handle;
78 const size_t needed = ser->offset + size;
79
80 while(needed > ser->size)
81 {
82 const size_t augmented = ser->size
83 ? ser->size << 1
84 : 1024;
85 uint8_t *grown = ser->realloc(ser->data, ser->buf, augmented);
86 if(!grown) // out-of-memory
87 {
88 return 0;
89 }
90
91 ser->buf = grown;
92 ser->size = augmented;
93 }
94
95 const LV2_Atom_Forge_Ref ref = ser->offset + 1;
96 memcpy(&ser->buf[ser->offset], buf, size);
97 ser->offset += size;
98
99 return ref;
100 }
101
102 static LV2_Atom *
_ser_atom_deref(LV2_Atom_Forge_Sink_Handle handle,LV2_Atom_Forge_Ref ref)103 _ser_atom_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
104 {
105 ser_atom_t *ser = handle;
106
107 if(!ref) // invalid reference
108 {
109 return NULL;
110 }
111
112 const size_t offset = ref - 1;
113 return (LV2_Atom *)&ser->buf[offset];
114 }
115
116 static void *
_ser_atom_realloc(void * data,void * buf,size_t size)117 _ser_atom_realloc(void *data, void *buf, size_t size)
118 {
119 (void)data;
120
121 return realloc(buf, size);
122 }
123
124 static void
_ser_atom_free(void * data,void * buf)125 _ser_atom_free(void *data, void *buf)
126 {
127 (void)data;
128
129 free(buf);
130 }
131
132 SER_ATOM_API int
ser_atom_funcs(ser_atom_t * ser,ser_atom_realloc_t realloc,ser_atom_free_t free,void * data)133 ser_atom_funcs(ser_atom_t *ser, ser_atom_realloc_t realloc,
134 ser_atom_free_t free, void *data)
135 {
136 if(!ser || !realloc || !free || ser_atom_deinit(ser))
137 {
138 return -1;
139 }
140
141 ser->realloc = realloc;
142 ser->free = free;
143 ser->data = data;
144
145 return 0;
146 }
147
148 SER_ATOM_API int
ser_atom_init(ser_atom_t * ser)149 ser_atom_init(ser_atom_t *ser)
150 {
151 if(!ser)
152 {
153 return -1;
154 }
155
156 ser->size = 0;
157 ser->offset = 0;
158 ser->buf = NULL;
159
160 return ser_atom_funcs(ser, _ser_atom_realloc, _ser_atom_free, NULL);
161 }
162
163 SER_ATOM_API int
ser_atom_reset(ser_atom_t * ser,LV2_Atom_Forge * forge)164 ser_atom_reset(ser_atom_t *ser, LV2_Atom_Forge *forge)
165 {
166 if(!ser || !forge)
167 {
168 return -1;
169 }
170
171 lv2_atom_forge_set_sink(forge, _ser_atom_sink, _ser_atom_deref, ser);
172
173 ser->offset = 0;
174
175 return 0;
176 }
177
178 SER_ATOM_API LV2_Atom *
ser_atom_get(ser_atom_t * ser)179 ser_atom_get(ser_atom_t *ser)
180 {
181 if(!ser)
182 {
183 return NULL;
184 }
185
186 return ser->atom;
187 }
188
189 SER_ATOM_API int
ser_atom_deinit(ser_atom_t * ser)190 ser_atom_deinit(ser_atom_t *ser)
191 {
192 if(!ser)
193 {
194 return -1;
195 }
196
197 if(ser->buf)
198 {
199 ser->free(ser->data, ser->buf);
200 }
201
202 ser->size = 0;
203 ser->offset = 0;
204 ser->buf = NULL;
205
206 return 0;
207 }
208
209 #endif // SER_ATOM_IMPLEMENTATION
210
211 #ifdef __cplusplus
212 }
213 #endif
214
215 #endif //_SER_ATOM_H
216