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