1 /*
2   Copyright 2012-2016 David Robillard <http://drobilla.net>
3 
4   Permission to use, copy, modify, and/or distribute this software for any
5   purpose with or without fee is hereby granted, provided that the above
6   copyright notice and this permission notice appear in all copies.
7 
8   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 
17 #include <assert.h>
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "lv2/atom-forge.h"
24 #include "lv2/atom-util.h"
25 #include "lv2/midi.h"
26 
27 #include "sratom/sratom.h"
28 
29 #define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#"
30 #define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#"
31 
32 #define USTR(str) ((const uint8_t*)(str))
33 
34 static const SerdStyle style = (SerdStyle)(
35 	SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED);
36 
37 typedef enum {
38 	MODE_SUBJECT,
39 	MODE_BODY,
40 	MODE_SEQUENCE
41 } ReadMode;
42 
43 struct SratomImpl {
44 	LV2_URID_Map*     map;
45 	LV2_Atom_Forge    forge;
46 	LV2_URID          atom_Event;
47 	LV2_URID          atom_frameTime;
48 	LV2_URID          atom_beatTime;
49 	LV2_URID          midi_MidiEvent;
50 	unsigned          next_id;
51 	SerdEnv*          env;
52 	SerdNode          base_uri;
53 	SerdURI           base;
54 	SerdStatementSink write_statement;
55 	SerdEndSink       end_anon;
56 	void*             handle;
57 	SratomObjectMode  object_mode;
58 	bool              pretty_numbers;
59 	uint32_t          seq_unit;
60 	struct {
61 		SordNode* atom_childType;
62 		SordNode* atom_frameTime;
63 		SordNode* atom_beatTime;
64 		SordNode* rdf_first;
65 		SordNode* rdf_rest;
66 		SordNode* rdf_type;
67 		SordNode* rdf_value;
68 		SordNode* xsd_base64Binary;
69 	} nodes;
70 };
71 
72 static void
73 read_node(Sratom*         sratom,
74           LV2_Atom_Forge* forge,
75           SordWorld*      world,
76           SordModel*      model,
77           const SordNode* node,
78           ReadMode        mode);
79 
80 SRATOM_API
81 Sratom*
sratom_new(LV2_URID_Map * map)82 sratom_new(LV2_URID_Map* map)
83 {
84 	Sratom* sratom = (Sratom*)calloc(1, sizeof(Sratom));
85 	if (sratom) {
86 		sratom->map            = map;
87 		sratom->atom_Event     = map->map(map->handle, LV2_ATOM__Event);
88 		sratom->atom_frameTime = map->map(map->handle, LV2_ATOM__frameTime);
89 		sratom->atom_beatTime  = map->map(map->handle, LV2_ATOM__beatTime);
90 		sratom->midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
91 		sratom->object_mode    = SRATOM_OBJECT_MODE_BLANK;
92 		lv2_atom_forge_init(&sratom->forge, map);
93 	}
94 	return sratom;
95 }
96 
97 SRATOM_API
98 void
sratom_free(Sratom * sratom)99 sratom_free(Sratom* sratom)
100 {
101 	serd_node_free(&sratom->base_uri);
102 	free(sratom);
103 }
104 
105 SRATOM_API
106 void
sratom_set_env(Sratom * sratom,SerdEnv * env)107 sratom_set_env(Sratom* sratom, SerdEnv* env)
108 {
109 	sratom->env = env;
110 }
111 
112 SRATOM_API
113 void
sratom_set_sink(Sratom * sratom,const char * base_uri,SerdStatementSink write_statement,SerdEndSink end_anon,void * handle)114 sratom_set_sink(Sratom*           sratom,
115                 const char*       base_uri,
116                 SerdStatementSink write_statement,
117                 SerdEndSink       end_anon,
118                 void*             handle)
119 {
120 	if (base_uri) {
121 		serd_node_free(&sratom->base_uri);
122 		sratom->base_uri = serd_node_new_uri_from_string(
123 			USTR(base_uri), NULL, NULL);
124 		serd_uri_parse(sratom->base_uri.buf, &sratom->base);
125 	}
126 	sratom->write_statement = write_statement;
127 	sratom->end_anon        = end_anon;
128 	sratom->handle          = handle;
129 }
130 
131 SRATOM_API
132 void
sratom_set_pretty_numbers(Sratom * sratom,bool pretty_numbers)133 sratom_set_pretty_numbers(Sratom* sratom,
134                           bool    pretty_numbers)
135 {
136 	sratom->pretty_numbers = pretty_numbers;
137 }
138 
139 SRATOM_API
140 void
sratom_set_object_mode(Sratom * sratom,SratomObjectMode object_mode)141 sratom_set_object_mode(Sratom*          sratom,
142                        SratomObjectMode object_mode)
143 {
144 	sratom->object_mode = object_mode;
145 }
146 
147 static void
gensym(SerdNode * out,char c,unsigned num)148 gensym(SerdNode* out, char c, unsigned num)
149 {
150 	out->n_bytes = out->n_chars = snprintf(
151 		(char*)out->buf, 10, "%c%u", c, num);
152 }
153 
154 static void
list_append(Sratom * sratom,LV2_URID_Unmap * unmap,unsigned * flags,SerdNode * s,SerdNode * p,SerdNode * node,uint32_t size,uint32_t type,const void * body)155 list_append(Sratom*           sratom,
156             LV2_URID_Unmap*   unmap,
157             unsigned*         flags,
158             SerdNode*         s,
159             SerdNode*         p,
160             SerdNode*         node,
161             uint32_t          size,
162             uint32_t          type,
163             const void*       body)
164 {
165 	// Generate a list node
166 	gensym(node, 'l', sratom->next_id);
167 	sratom->write_statement(sratom->handle, *flags, NULL,
168 	                        s, p, node, NULL, NULL);
169 
170 	// _:node rdf:first value
171 	*flags = SERD_LIST_CONT;
172 	*p = serd_node_from_string(SERD_URI, NS_RDF "first");
173 	sratom_write(sratom, unmap, *flags, node, p, type, size, body);
174 
175 	// Set subject to node and predicate to rdf:rest for next time
176 	gensym(node, 'l', ++sratom->next_id);
177 	*s = *node;
178 	*p = serd_node_from_string(SERD_URI, NS_RDF "rest");
179 }
180 
181 static void
list_end(SerdStatementSink sink,void * handle,unsigned * flags,SerdNode * s,SerdNode * p)182 list_end(SerdStatementSink sink,
183          void*             handle,
184          unsigned*         flags,
185          SerdNode*         s,
186          SerdNode*         p)
187 {
188 	// _:node rdf:rest rdf:nil
189 	const SerdNode nil = serd_node_from_string(SERD_URI, NS_RDF "nil");
190 	sink(handle, *flags, NULL, s, p, &nil, NULL, NULL);
191 }
192 
193 static void
start_object(Sratom * sratom,uint32_t * flags,const SerdNode * subject,const SerdNode * predicate,const SerdNode * node,const char * type)194 start_object(Sratom*         sratom,
195              uint32_t*       flags,
196              const SerdNode* subject,
197              const SerdNode* predicate,
198              const SerdNode* node,
199              const char*     type)
200 {
201 	if (subject && predicate) {
202 		sratom->write_statement(sratom->handle, *flags|SERD_ANON_O_BEGIN, NULL,
203 		                        subject, predicate, node, NULL, NULL);
204 		// Start abbreviating object properties
205 		*flags |= SERD_ANON_CONT;
206 
207 		// Object is in a list, stop list abbreviating if necessary
208 		*flags &= ~SERD_LIST_CONT;
209 	}
210 	if (type) {
211 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "type");
212 		SerdNode o = serd_node_from_string(SERD_URI, USTR(type));
213 		sratom->write_statement(sratom->handle, *flags, NULL,
214 		                        node, &p, &o, NULL, NULL);
215 	}
216 }
217 
218 static bool
path_is_absolute(const char * path)219 path_is_absolute(const char* path)
220 {
221 	return (path[0] == '/'
222 	        || (isalpha(path[0]) && path[1] == ':'
223 	            && (path[2] == '/' || path[2] == '\\')));
224 }
225 
226 static SerdNode
number_type(const Sratom * sratom,const uint8_t * type)227 number_type(const Sratom* sratom, const uint8_t* type)
228 {
229 	if (sratom->pretty_numbers &&
230 	    (!strcmp((const char*)type, (const char*)NS_XSD "int") ||
231 	     !strcmp((const char*)type, (const char*)NS_XSD "long"))) {
232 		return serd_node_from_string(SERD_URI, NS_XSD "integer");
233 	} else if (sratom->pretty_numbers &&
234 	           (!strcmp((const char*)type, (const char*)NS_XSD "float") ||
235 	            !strcmp((const char*)type, (const char*)NS_XSD "double"))) {
236 		return serd_node_from_string(SERD_URI, NS_XSD "decimal");
237 	} else {
238 		return serd_node_from_string(SERD_URI, (const uint8_t*)type);
239 	}
240 }
241 
242 SRATOM_API
243 int
sratom_write(Sratom * sratom,LV2_URID_Unmap * unmap,uint32_t flags,const SerdNode * subject,const SerdNode * predicate,uint32_t type_urid,uint32_t size,const void * body)244 sratom_write(Sratom*         sratom,
245              LV2_URID_Unmap* unmap,
246              uint32_t        flags,
247              const SerdNode* subject,
248              const SerdNode* predicate,
249              uint32_t        type_urid,
250              uint32_t        size,
251              const void*     body)
252 {
253 	const char* const type        = unmap->unmap(unmap->handle, type_urid);
254 	uint8_t           idbuf[12]   = "b0000000000";
255 	SerdNode          id          = serd_node_from_string(SERD_BLANK, idbuf);
256 	uint8_t           nodebuf[12] = "b0000000000";
257 	SerdNode          node        = serd_node_from_string(SERD_BLANK, nodebuf);
258 	SerdNode          object      = SERD_NODE_NULL;
259 	SerdNode          datatype    = SERD_NODE_NULL;
260 	SerdNode          language    = SERD_NODE_NULL;
261 	bool              new_node    = false;
262 	if (type_urid == 0 && size == 0) {
263 		object = serd_node_from_string(SERD_URI, USTR(NS_RDF "nil"));
264 	} else if (type_urid == sratom->forge.String) {
265 		object = serd_node_from_string(SERD_LITERAL, (const uint8_t*)body);
266 	} else if (type_urid == sratom->forge.Chunk) {
267 		datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
268 		object   = serd_node_new_blob(body, size, true);
269 		new_node = true;
270 	} else if (type_urid == sratom->forge.Literal) {
271 		const LV2_Atom_Literal_Body* lit = (const LV2_Atom_Literal_Body*)body;
272 		const uint8_t*         str = USTR(lit + 1);
273 		object = serd_node_from_string(SERD_LITERAL, str);
274 		if (lit->datatype) {
275 			datatype = serd_node_from_string(
276 				SERD_URI, USTR(unmap->unmap(unmap->handle, lit->datatype)));
277 		} else if (lit->lang) {
278 			const char*  lang       = unmap->unmap(unmap->handle, lit->lang);
279 			const char*  prefix     = "http://lexvo.org/id/iso639-3/";
280 			const size_t prefix_len = strlen(prefix);
281 			if (lang && !strncmp(lang, prefix, prefix_len)) {
282 				language = serd_node_from_string(
283 					SERD_LITERAL, USTR(lang + prefix_len));
284 			} else {
285 				fprintf(stderr, "Unknown language URID %d\n", lit->lang);
286 			}
287 		}
288 	} else if (type_urid == sratom->forge.URID) {
289 		const uint32_t urid = *(const uint32_t*)body;
290 		const uint8_t* str  = USTR(unmap->unmap(unmap->handle, urid));
291 		object = serd_node_from_string(SERD_URI, str);
292 	} else if (type_urid == sratom->forge.Path) {
293 		const uint8_t* str = USTR(body);
294 		if (path_is_absolute((const char*)str)) {
295 			new_node = true;
296 			object   = serd_node_new_file_uri(str, NULL, NULL, true);
297 		} else {
298 			if (!sratom->base_uri.buf ||
299 			    strncmp((const char*)sratom->base_uri.buf, "file://", 7)) {
300 				fprintf(stderr, "warning: Relative path but base is not a file URI.\n");
301 				fprintf(stderr, "warning: Writing ambiguous atom:Path literal.\n");
302 				object   = serd_node_from_string(SERD_LITERAL, str);
303 				datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path));
304 			} else {
305 				new_node = true;
306 				SerdNode rel = serd_node_new_file_uri(str, NULL, NULL, true);
307 				object = serd_node_new_uri_from_node(&rel, &sratom->base, NULL);
308 				serd_node_free(&rel);
309 			}
310 		}
311 	} else if (type_urid == sratom->forge.URI) {
312 		const uint8_t* str = USTR(body);
313 		object = serd_node_from_string(SERD_URI, str);
314 	} else if (type_urid == sratom->forge.Int) {
315 		new_node = true;
316 		object   = serd_node_new_integer(*(const int32_t*)body);
317 		datatype = number_type(sratom, NS_XSD "int");
318 	} else if (type_urid == sratom->forge.Long) {
319 		new_node = true;
320 		object   = serd_node_new_integer(*(const int64_t*)body);
321 		datatype = number_type(sratom, NS_XSD "long");
322 	} else if (type_urid == sratom->forge.Float) {
323 		new_node = true;
324 		object   = serd_node_new_decimal(*(const float*)body, 8);
325 		datatype = number_type(sratom, NS_XSD "float");
326 	} else if (type_urid == sratom->forge.Double) {
327 		new_node = true;
328 		object   = serd_node_new_decimal(*(const double*)body, 16);
329 		datatype = number_type(sratom, NS_XSD "double");
330 	} else if (type_urid == sratom->forge.Bool) {
331 		const int32_t val = *(const int32_t*)body;
332 		datatype = serd_node_from_string(SERD_URI, NS_XSD "boolean");
333 		object   = serd_node_from_string(SERD_LITERAL,
334 		                                 USTR(val ? "true" : "false"));
335 	} else if (type_urid == sratom->midi_MidiEvent) {
336 		new_node = true;
337 		datatype = serd_node_from_string(SERD_URI, USTR(LV2_MIDI__MidiEvent));
338 		uint8_t* str = (uint8_t*)calloc(size * 2 + 1, 1);
339 		for (uint32_t i = 0; i < size; ++i) {
340 			snprintf((char*)str + (2 * i), size * 2 + 1, "%02X",
341 			         (unsigned)(uint8_t)*((const uint8_t*)body + i));
342 		}
343 		object = serd_node_from_string(SERD_LITERAL, USTR(str));
344 	} else if (type_urid == sratom->atom_Event) {
345 		const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body;
346 		gensym(&id, 'e', sratom->next_id++);
347 		start_object(sratom, &flags, subject, predicate, &id, NULL);
348 		SerdNode time;
349 		SerdNode p;
350 		if (sratom->seq_unit == sratom->atom_beatTime) {
351 			time     = serd_node_new_decimal(ev->time.beats, 16);
352 			p        = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__beatTime));
353 			datatype = number_type(sratom, NS_XSD "double");
354 		} else {
355 			time     = serd_node_new_integer(ev->time.frames);
356 			p        = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__frameTime));
357 			datatype = number_type(sratom, NS_XSD "long");
358 		}
359 		sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL,
360 		                        &id, &p, &time, &datatype, &language);
361 		serd_node_free(&time);
362 
363 		p = serd_node_from_string(SERD_URI, NS_RDF "value");
364 		sratom_write(sratom, unmap, SERD_ANON_CONT, &id, &p,
365 		             ev->body.type, ev->body.size, LV2_ATOM_BODY(&ev->body));
366 		if (sratom->end_anon) {
367 			sratom->end_anon(sratom->handle, &id);
368 		}
369 	} else if (type_urid == sratom->forge.Tuple) {
370 		gensym(&id, 't', sratom->next_id++);
371 		start_object(sratom, &flags, subject, predicate, &id, type);
372 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
373 		flags |= SERD_LIST_O_BEGIN;
374 		LV2_ATOM_TUPLE_BODY_FOREACH(body, size, i) {
375 			list_append(sratom, unmap, &flags, &id, &p, &node,
376 			            i->size, i->type, LV2_ATOM_BODY(i));
377 		}
378 		list_end(sratom->write_statement, sratom->handle, &flags, &id, &p);
379 		if (sratom->end_anon) {
380 			sratom->end_anon(sratom->handle, &id);
381 		}
382 	} else if (type_urid == sratom->forge.Vector) {
383 		const LV2_Atom_Vector_Body* vec  = (const LV2_Atom_Vector_Body*)body;
384 		gensym(&id, 'v', sratom->next_id++);
385 		start_object(sratom, &flags, subject, predicate, &id, type);
386 		SerdNode p = serd_node_from_string(SERD_URI, (const uint8_t*)LV2_ATOM__childType);
387 		SerdNode child_type = serd_node_from_string(
388 			SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, vec->child_type));
389 		sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &child_type, NULL, NULL);
390 		p = serd_node_from_string(SERD_URI, NS_RDF "value");
391 		flags |= SERD_LIST_O_BEGIN;
392 		for (const char* i = (const char*)(vec + 1);
393 		     i < (const char*)vec + size;
394 		     i += vec->child_size) {
395 			list_append(sratom, unmap, &flags, &id, &p, &node,
396 			            vec->child_size, vec->child_type, i);
397 		}
398 		list_end(sratom->write_statement, sratom->handle, &flags, &id, &p);
399 		if (sratom->end_anon) {
400 			sratom->end_anon(sratom->handle, &id);
401 		}
402 	} else if (lv2_atom_forge_is_object_type(&sratom->forge, type_urid)) {
403 		const LV2_Atom_Object_Body* obj   = (const LV2_Atom_Object_Body*)body;
404 		const char*                 otype = unmap->unmap(unmap->handle,
405 		                                                 obj->otype);
406 
407 		if (lv2_atom_forge_is_blank(&sratom->forge, type_urid, obj)) {
408 			gensym(&id, 'b', sratom->next_id++);
409 			start_object(sratom, &flags, subject, predicate, &id, otype);
410 		} else {
411 			id = serd_node_from_string(
412 				SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, obj->id));
413 			flags = 0;
414 			start_object(sratom, &flags, NULL, NULL, &id, otype);
415 		}
416 		LV2_ATOM_OBJECT_BODY_FOREACH(obj, size, prop) {
417 			const char* const key  = unmap->unmap(unmap->handle, prop->key);
418 			SerdNode          pred = serd_node_from_string(SERD_URI, USTR(key));
419 			sratom_write(sratom, unmap, flags, &id, &pred,
420 			             prop->value.type, prop->value.size,
421 			             LV2_ATOM_BODY(&prop->value));
422 		}
423 		if (sratom->end_anon && (flags & SERD_ANON_CONT)) {
424 			sratom->end_anon(sratom->handle, &id);
425 		}
426 	} else if (type_urid == sratom->forge.Sequence) {
427 		const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body;
428 		gensym(&id, 'v', sratom->next_id++);
429 		start_object(sratom, &flags, subject, predicate, &id, type);
430 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
431 		flags |= SERD_LIST_O_BEGIN;
432 		LV2_ATOM_SEQUENCE_BODY_FOREACH(seq, size, ev) {
433 			sratom->seq_unit = seq->unit;
434 			list_append(sratom, unmap, &flags, &id, &p, &node,
435 			            sizeof(LV2_Atom_Event) + ev->body.size,
436 			            sratom->atom_Event,
437 			            ev);
438 		}
439 		list_end(sratom->write_statement, sratom->handle, &flags, &id, &p);
440 		if (sratom->end_anon && subject && predicate) {
441 			sratom->end_anon(sratom->handle, &id);
442 		}
443 	} else {
444 		gensym(&id, 'b', sratom->next_id++);
445 		start_object(sratom, &flags, subject, predicate, &id, type);
446 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
447 		SerdNode o = serd_node_new_blob(body, size, true);
448 		datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
449 		sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &o, &datatype, NULL);
450 		if (sratom->end_anon && subject && predicate) {
451 			sratom->end_anon(sratom->handle, &id);
452 		}
453 		serd_node_free(&o);
454 	}
455 
456 	if (object.buf) {
457 		SerdNode def_s = serd_node_from_string(SERD_BLANK, USTR("atom"));
458 		SerdNode def_p = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
459 		if (!subject) {
460 			subject = &def_s;
461 		}
462 		if (!predicate) {
463 			predicate = &def_p;
464 		}
465 		sratom->write_statement(sratom->handle, flags, NULL,
466 		                        subject, predicate, &object, &datatype, &language);
467 	}
468 
469 	if (new_node) {
470 		serd_node_free(&object);
471 	}
472 
473 	return 0;
474 }
475 
476 SRATOM_API
477 char*
sratom_to_turtle(Sratom * sratom,LV2_URID_Unmap * unmap,const char * base_uri,const SerdNode * subject,const SerdNode * predicate,uint32_t type,uint32_t size,const void * body)478 sratom_to_turtle(Sratom*         sratom,
479                  LV2_URID_Unmap* unmap,
480                  const char*     base_uri,
481                  const SerdNode* subject,
482                  const SerdNode* predicate,
483                  uint32_t        type,
484                  uint32_t        size,
485                  const void*     body)
486 {
487 	SerdURI     buri   = SERD_URI_NULL;
488 	SerdNode    base   = serd_node_new_uri_from_string(USTR(base_uri), &sratom->base, &buri);
489 	SerdEnv*    env    = sratom->env ? sratom->env : serd_env_new(NULL);
490 	SerdChunk   str    = { NULL, 0 };
491 	SerdWriter* writer = serd_writer_new(
492 		SERD_TURTLE, style, env, &buri, serd_chunk_sink, &str);
493 
494 	serd_env_set_base_uri(env, &base);
495 	sratom_set_sink(sratom, base_uri,
496 	                (SerdStatementSink)serd_writer_write_statement,
497 	                (SerdEndSink)serd_writer_end_anon,
498 	                writer);
499 	sratom_write(sratom, unmap, SERD_EMPTY_S,
500 	             subject, predicate, type, size, body);
501 	serd_writer_finish(writer);
502 
503 	serd_writer_free(writer);
504 	if (!sratom->env) {
505 		serd_env_free(env);
506 	}
507 	serd_node_free(&base);
508 	return (char*)serd_chunk_sink_finish(&str);
509 }
510 
511 static void
read_list_value(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,ReadMode mode)512 read_list_value(Sratom*         sratom,
513                 LV2_Atom_Forge* forge,
514                 SordWorld*      world,
515                 SordModel*      model,
516                 const SordNode* node,
517                 ReadMode        mode)
518 {
519 	SordNode* fst = sord_get(model, node, sratom->nodes.rdf_first, NULL, NULL);
520 	SordNode* rst = sord_get(model, node, sratom->nodes.rdf_rest, NULL, NULL);
521 	if (fst && rst) {
522 		read_node(sratom, forge, world, model, fst, mode);
523 		read_list_value(sratom, forge, world, model, rst, mode);
524 	}
525 	sord_node_free(world, rst);
526 	sord_node_free(world, fst);
527 }
528 
529 static void
read_resource(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,LV2_URID otype)530 read_resource(Sratom*         sratom,
531               LV2_Atom_Forge* forge,
532               SordWorld*      world,
533               SordModel*      model,
534               const SordNode* node,
535               LV2_URID        otype)
536 {
537 	LV2_URID_Map* map = sratom->map;
538 	SordQuad      q   = { node, NULL, NULL, NULL };
539 	SordIter*     i   = sord_find(model, q);
540 	SordQuad      match;
541 	for (; !sord_iter_end(i); sord_iter_next(i)) {
542 		sord_iter_get(i, match);
543 		const SordNode* p      = match[SORD_PREDICATE];
544 		const SordNode* o      = match[SORD_OBJECT];
545 		const char*     p_uri  = (const char*)sord_node_get_string(p);
546 		uint32_t        p_urid = map->map(map->handle, p_uri);
547 		if (!(sord_node_equals(p, sratom->nodes.rdf_type) &&
548 		      sord_node_get_type(o) == SORD_URI &&
549 		      map->map(map->handle, (const char*)sord_node_get_string(o)) == otype)) {
550 			lv2_atom_forge_key(forge, p_urid);
551 			read_node(sratom, forge, world, model, o, MODE_BODY);
552 		}
553 	}
554 	sord_iter_free(i);
555 }
556 
557 static uint32_t
atom_size(Sratom * sratom,uint32_t type_urid)558 atom_size(Sratom* sratom, uint32_t type_urid)
559 {
560 	if (type_urid == sratom->forge.Int) {
561 		return sizeof(int32_t);
562 	} else if (type_urid == sratom->forge.Long) {
563 		return sizeof(int64_t);
564 	} else if (type_urid == sratom->forge.Float) {
565 		return sizeof(float);
566 	} else if (type_urid == sratom->forge.Double) {
567 		return sizeof(double);
568 	} else if (type_urid == sratom->forge.Bool) {
569 		return sizeof(int32_t);
570 	} else if (type_urid == sratom->forge.URID) {
571 		return sizeof(uint32_t);
572 	}
573 	return 0;
574 }
575 
576 static void
read_node(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,ReadMode mode)577 read_node(Sratom*         sratom,
578           LV2_Atom_Forge* forge,
579           SordWorld*      world,
580           SordModel*      model,
581           const SordNode* node,
582           ReadMode        mode)
583 {
584 	LV2_URID_Map* map = sratom->map;
585 	size_t        len = 0;
586 	const char*   str = (const char*)sord_node_get_string_counted(node, &len);
587 	if (sord_node_get_type(node) == SORD_LITERAL) {
588 		SordNode*   datatype = sord_node_get_datatype(node);
589 		const char* language = sord_node_get_language(node);
590 		if (datatype) {
591 			const char* type_uri = (const char*)sord_node_get_string(datatype);
592 			if (!strcmp(type_uri, (const char*)NS_XSD "int") ||
593 			    !strcmp(type_uri, (const char*)NS_XSD "integer")) {
594 				lv2_atom_forge_int(forge, strtol(str, NULL, 10));
595 			} else if (!strcmp(type_uri, (const char*)NS_XSD "long")) {
596 				lv2_atom_forge_long(forge, strtol(str, NULL, 10));
597 			} else if (!strcmp(type_uri, (const char*)NS_XSD "float") ||
598 			           !strcmp(type_uri, (const char*)NS_XSD "decimal")) {
599 				lv2_atom_forge_float(forge, serd_strtod(str, NULL));
600 			} else if (!strcmp(type_uri, (const char*)NS_XSD "double")) {
601 				lv2_atom_forge_double(forge, serd_strtod(str, NULL));
602 			} else if (!strcmp(type_uri, (const char*)NS_XSD "boolean")) {
603 				lv2_atom_forge_bool(forge, !strcmp(str, "true"));
604 			} else if (!strcmp(type_uri, (const char*)NS_XSD "base64Binary")) {
605 				size_t size = 0;
606 				void*  body = serd_base64_decode(USTR(str), len, &size);
607 				lv2_atom_forge_atom(forge, size, forge->Chunk);
608 				lv2_atom_forge_write(forge, body, size);
609 				free(body);
610 			} else if (!strcmp(type_uri, LV2_ATOM__Path)) {
611 				const size_t str_len = str ? strlen(str) : 0;
612 				lv2_atom_forge_path(forge, str, str_len);
613 			} else if (!strcmp(type_uri, LV2_MIDI__MidiEvent)) {
614 				lv2_atom_forge_atom(forge, len / 2, sratom->midi_MidiEvent);
615 				for (const char* s = str; s < str + len; s += 2) {
616 					unsigned num;
617 					sscanf(s, "%2X", &num);
618 					const uint8_t c = num;
619 					lv2_atom_forge_raw(forge, &c, 1);
620 				}
621 				lv2_atom_forge_pad(forge, len / 2);
622 			} else {
623 				const size_t str_len = str ? strlen(str) : 0;
624 				lv2_atom_forge_literal(
625 					forge, str, str_len,
626 					sratom->map->map(sratom->map->handle, type_uri),
627 					0);
628 			}
629 		} else if (language) {
630 			const char*  prefix   = "http://lexvo.org/id/iso639-3/";
631 			const size_t lang_len = strlen(prefix) + strlen(language);
632 			char*        lang_uri = (char*)calloc(lang_len + 1, 1);
633 			const size_t str_len = str ? strlen(str) : 0;
634 			snprintf(lang_uri, lang_len + 1, "%s%s", prefix, language);
635 			lv2_atom_forge_literal(
636 				forge, str, str_len, 0,
637 				sratom->map->map(sratom->map->handle, lang_uri));
638 			free(lang_uri);
639 		} else {
640 			const size_t str_len = str ? strlen(str) : 0;
641 			lv2_atom_forge_string(forge, str, str_len);
642 		}
643 	} else if (sord_node_get_type(node) == SORD_URI &&
644 	           !(sratom->object_mode == SRATOM_OBJECT_MODE_BLANK_SUBJECT
645 	             && mode == MODE_SUBJECT)) {
646 		if (!strcmp(str, (const char*)NS_RDF "nil")) {
647 			lv2_atom_forge_atom(forge, 0, 0);
648 		} else if (!strncmp(str, "file://", 7)) {
649 			SerdURI uri;
650 			serd_uri_parse((const uint8_t*)str, &uri);
651 
652 			SerdNode rel  = serd_node_new_relative_uri(&uri, &sratom->base, NULL, NULL);
653 			uint8_t* path = serd_file_uri_parse(rel.buf, NULL);
654 			lv2_atom_forge_path(forge, (const char*)path, strlen((const char*)path));
655 			free(path);
656 			serd_node_free(&rel);
657 		} else {
658 			lv2_atom_forge_urid(forge, map->map(map->handle, str));
659 		}
660 	} else {
661 		SordNode* type = sord_get(
662 			model, node, sratom->nodes.rdf_type, NULL, NULL);
663 		SordNode* value = sord_get(
664 			model, node, sratom->nodes.rdf_value, NULL, NULL);
665 
666 		const uint8_t* type_uri  = NULL;
667 		uint32_t       type_urid = 0;
668 		if (type) {
669 			type_uri  = sord_node_get_string(type);
670 			type_urid = map->map(map->handle, (const char*)type_uri);
671 		}
672 
673 		LV2_Atom_Forge_Frame frame = { 0, 0 };
674 		if (mode == MODE_SEQUENCE) {
675 			SordNode* time = sord_get(
676 				model, node, sratom->nodes.atom_beatTime, NULL, NULL);
677 			uint32_t seq_unit;
678 			if (time) {
679 				const char* time_str = (const char*)sord_node_get_string(time);
680 				lv2_atom_forge_beat_time(forge, serd_strtod(time_str, NULL));
681 				seq_unit = sratom->atom_beatTime;
682 			} else {
683 				time = sord_get(model, node, sratom->nodes.atom_frameTime, NULL, NULL);
684 				const char* time_str = time
685 					? (const char*)sord_node_get_string(time)
686 					: "";
687 				lv2_atom_forge_frame_time(forge, serd_strtod(time_str, NULL));
688 				seq_unit = sratom->atom_frameTime;
689 			}
690 			read_node(sratom, forge, world, model, value, MODE_BODY);
691 			sord_node_free(world, time);
692 			sratom->seq_unit = seq_unit;
693 		} else if (type_urid == sratom->forge.Tuple) {
694 			lv2_atom_forge_tuple(forge, &frame);
695 			read_list_value(sratom, forge, world, model, value, MODE_BODY);
696 		} else if (type_urid == sratom->forge.Sequence) {
697 			const LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
698 			sratom->seq_unit = 0;
699 			read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE);
700 
701 			LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)lv2_atom_forge_deref(forge, ref);
702 			seq->body.unit = (sratom->seq_unit == sratom->atom_frameTime) ? 0 : sratom->seq_unit;
703 		} else if (type_urid == sratom->forge.Vector) {
704 			SordNode* child_type_node = sord_get(
705 				model, node, sratom->nodes.atom_childType, NULL, NULL);
706 			uint32_t child_type = map->map(
707 				map->handle, (const char*)sord_node_get_string(child_type_node));
708 			uint32_t child_size = atom_size(sratom, child_type);
709 			if (child_size > 0) {
710 				LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector_head(
711 					forge, &frame, child_size, child_type);
712 				read_list_value(sratom, forge, world, model, value, MODE_BODY);
713 				lv2_atom_forge_pop(forge, &frame);
714 				frame.ref = 0;
715 				lv2_atom_forge_pad(forge, lv2_atom_forge_deref(forge, ref)->size);
716 			}
717 			sord_node_free(world, child_type_node);
718 		} else if (value && sord_node_equals(sord_node_get_datatype(value),
719 		                                     sratom->nodes.xsd_base64Binary)) {
720 			size_t         vlen = 0;
721 			const uint8_t* vstr = sord_node_get_string_counted(value, &vlen);
722 			size_t         size = 0;
723 			void*          body = serd_base64_decode(vstr, vlen, &size);
724 			lv2_atom_forge_atom(forge, size, type_urid);
725 			lv2_atom_forge_write(forge, body, size);
726 			free(body);
727 		} else if (sord_node_get_type(node) == SORD_URI) {
728 			lv2_atom_forge_object(
729 				forge, &frame, map->map(map->handle, str), type_urid);
730 			read_resource(sratom, forge, world, model, node, type_urid);
731 		} else {
732 			lv2_atom_forge_object(forge, &frame, 0, type_urid);
733 			read_resource(sratom, forge, world, model, node, type_urid);
734 		}
735 
736 		if (frame.ref) {
737 			lv2_atom_forge_pop(forge, &frame);
738 		}
739 		sord_node_free(world, value);
740 		sord_node_free(world, type);
741 	}
742 }
743 
744 SRATOM_API
745 void
sratom_read(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node)746 sratom_read(Sratom*         sratom,
747             LV2_Atom_Forge* forge,
748             SordWorld*      world,
749             SordModel*      model,
750             const SordNode* node)
751 {
752 	sratom->nodes.atom_childType   = sord_new_uri(world, USTR(LV2_ATOM__childType));
753 	sratom->nodes.atom_frameTime   = sord_new_uri(world, USTR(LV2_ATOM__frameTime));
754 	sratom->nodes.atom_beatTime    = sord_new_uri(world, USTR(LV2_ATOM__beatTime));
755 	sratom->nodes.rdf_first        = sord_new_uri(world, NS_RDF "first");
756 	sratom->nodes.rdf_rest         = sord_new_uri(world, NS_RDF "rest");
757 	sratom->nodes.rdf_type         = sord_new_uri(world, NS_RDF "type");
758 	sratom->nodes.rdf_value        = sord_new_uri(world, NS_RDF "value");
759 	sratom->nodes.xsd_base64Binary = sord_new_uri(world, NS_XSD "base64Binary");
760 
761 	sratom->next_id = 1;
762 	read_node(sratom, forge, world, model, node, MODE_SUBJECT);
763 
764 	sord_node_free(world, sratom->nodes.xsd_base64Binary);
765 	sord_node_free(world, sratom->nodes.rdf_value);
766 	sord_node_free(world, sratom->nodes.rdf_type);
767 	sord_node_free(world, sratom->nodes.rdf_rest);
768 	sord_node_free(world, sratom->nodes.rdf_first);
769 	sord_node_free(world, sratom->nodes.atom_frameTime);
770 	sord_node_free(world, sratom->nodes.atom_beatTime);
771 	sord_node_free(world, sratom->nodes.atom_childType);
772 	memset(&sratom->nodes, 0, sizeof(sratom->nodes));
773 }
774 
775 SRATOM_API
776 LV2_Atom_Forge_Ref
sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,const void * buf,uint32_t size)777 sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,
778                   const void*                buf,
779                   uint32_t                   size)
780 {
781 	SerdChunk*               chunk = (SerdChunk*)handle;
782 	const LV2_Atom_Forge_Ref ref   = chunk->len + 1;
783 	serd_chunk_sink(buf, size, chunk);
784 	return ref;
785 }
786 
787 SRATOM_API
788 LV2_Atom*
sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle,LV2_Atom_Forge_Ref ref)789 sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
790 {
791 	SerdChunk* chunk = (SerdChunk*)handle;
792 	return (LV2_Atom*)(chunk->buf + ref - 1);
793 }
794 
795 SRATOM_API
796 LV2_Atom*
sratom_from_turtle(Sratom * sratom,const char * base_uri,const SerdNode * subject,const SerdNode * predicate,const char * str)797 sratom_from_turtle(Sratom*         sratom,
798                    const char*     base_uri,
799                    const SerdNode* subject,
800                    const SerdNode* predicate,
801                    const char*     str)
802 {
803 	SerdChunk   out    = { NULL, 0 };
804 	SerdNode    base   = serd_node_new_uri_from_string(USTR(base_uri), &sratom->base, NULL);
805 	SordWorld*  world  = sord_world_new();
806 	SordModel*  model  = sord_new(world, SORD_SPO, false);
807 	SerdEnv*    env    = sratom->env ? sratom->env : serd_env_new(&base);
808 	SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
809 
810 	if (!serd_reader_read_string(reader, (const uint8_t*)str)) {
811 		SordNode* s = sord_node_from_serd_node(world, env, subject, 0, 0);
812 		lv2_atom_forge_set_sink(
813 			&sratom->forge, sratom_forge_sink, sratom_forge_deref, &out);
814 		if (subject && predicate) {
815 			SordNode* p = sord_node_from_serd_node(world, env, predicate, 0, 0);
816 			SordNode* o = sord_get(model, s, p, NULL, NULL);
817 			if (o) {
818 				sratom_read(sratom, &sratom->forge, world, model, o);
819 				sord_node_free(world, o);
820 			} else {
821 				fprintf(stderr, "Failed to find node\n");
822 			}
823 		} else {
824 			sratom_read(sratom, &sratom->forge, world, model, s);
825 		}
826 	} else {
827 		fprintf(stderr, "Failed to read Turtle\n");
828 	}
829 
830 	serd_reader_free(reader);
831 	if (!sratom->env) {
832 		serd_env_free(env);
833 	}
834 	sord_free(model);
835 	sord_world_free(world);
836 	serd_node_free(&base);
837 
838 	return (LV2_Atom*)out.buf;
839 }
840