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