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