Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

654 lines
17KB

  1. /*
  2. Copyright 2011-2013 David Robillard <http://drobilla.net>
  3. Permission to use, copy, modify, and/or distribute this software for any
  4. purpose with or without fee is hereby granted, provided that the above
  5. copyright notice and this permission notice appear in all copies.
  6. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  7. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  8. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  9. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  10. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  11. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  12. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  13. */
  14. /**
  15. @file sordmm.hpp
  16. Public Sord C++ API.
  17. */
  18. #ifndef SORD_SORDMM_HPP
  19. #define SORD_SORDMM_HPP
  20. #include <cassert>
  21. #include <cstring>
  22. #include <cstdlib>
  23. #include <iostream>
  24. #include <set>
  25. #include <string>
  26. #include <sstream>
  27. #include "serd/serd.h"
  28. #include "sord/sord.h"
  29. #define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#"
  30. namespace Sord {
  31. /** Utility base class to prevent copying. */
  32. class Noncopyable {
  33. protected:
  34. Noncopyable() {}
  35. ~Noncopyable() {}
  36. private:
  37. Noncopyable(const Noncopyable&);
  38. const Noncopyable& operator=(const Noncopyable&);
  39. };
  40. /** C++ wrapper for a Sord object. */
  41. template <typename T>
  42. class Wrapper {
  43. public:
  44. inline Wrapper(T c_obj = NULL) : _c_obj(c_obj) {}
  45. inline T c_obj() { return _c_obj; }
  46. inline const T c_obj() const { return _c_obj; }
  47. protected:
  48. T _c_obj;
  49. };
  50. /** Collection of RDF namespaces with prefixes. */
  51. class Namespaces : public Wrapper<SerdEnv*> {
  52. public:
  53. Namespaces() : Wrapper<SerdEnv*>(serd_env_new(NULL)) {}
  54. ~Namespaces() { serd_env_free(_c_obj); }
  55. static inline SerdNode string_to_node(SerdType type, const std::string& s) {
  56. SerdNode ret = {
  57. (const uint8_t*)s.c_str(), s.length(), s.length(), 0, type };
  58. return ret;
  59. }
  60. inline void add(const std::string& name,
  61. const std::string& uri) {
  62. const SerdNode name_node = string_to_node(SERD_LITERAL, name);
  63. const SerdNode uri_node = string_to_node(SERD_URI, uri);
  64. serd_env_set_prefix(_c_obj, &name_node, &uri_node);
  65. }
  66. inline std::string qualify(std::string uri) const {
  67. const SerdNode uri_node = string_to_node(SERD_URI, uri);
  68. SerdNode prefix;
  69. SerdChunk suffix;
  70. if (serd_env_qualify(_c_obj, &uri_node, &prefix, &suffix)) {
  71. std::string ret((const char*)prefix.buf, prefix.n_bytes);
  72. ret.append(":").append((const char*)suffix.buf, suffix.len);
  73. return ret;
  74. }
  75. return uri;
  76. }
  77. inline std::string expand(const std::string& curie) const {
  78. assert(curie.find(":") != std::string::npos);
  79. SerdNode curie_node = string_to_node(SERD_CURIE, curie);
  80. SerdChunk uri_prefix;
  81. SerdChunk uri_suffix;
  82. if (!serd_env_expand(_c_obj, &curie_node, &uri_prefix, &uri_suffix)) {
  83. std::string ret((const char*)uri_prefix.buf, uri_prefix.len);
  84. ret.append((const char*)uri_suffix.buf, uri_suffix.len);
  85. return ret;
  86. }
  87. std::cerr << "CURIE `" << curie << "' has unknown prefix." << std::endl;
  88. return curie;
  89. }
  90. };
  91. /** Sord library state. */
  92. class World : public Noncopyable, public Wrapper<SordWorld*> {
  93. public:
  94. inline World()
  95. : _next_blank_id(0)
  96. {
  97. _c_obj = sord_world_new();
  98. }
  99. inline ~World() {
  100. sord_world_free(_c_obj);
  101. }
  102. inline uint64_t blank_id() { return _next_blank_id++; }
  103. inline void add_prefix(const std::string& prefix, const std::string& uri) {
  104. _prefixes.add(prefix, uri);
  105. }
  106. inline const Namespaces& prefixes() const { return _prefixes; }
  107. inline SordWorld* world() { return _c_obj; }
  108. private:
  109. Namespaces _prefixes;
  110. std::set<std::string> _blank_ids;
  111. uint64_t _next_blank_id;
  112. };
  113. /** An RDF Node (resource, literal, etc)
  114. */
  115. class Node : public Wrapper<SordNode*> {
  116. public:
  117. enum Type {
  118. UNKNOWN = 0,
  119. URI = SORD_URI,
  120. BLANK = SORD_BLANK,
  121. LITERAL = SORD_LITERAL
  122. };
  123. inline Node() : Wrapper<SordNode*>(NULL), _world(NULL) {}
  124. inline Node(World& world, Type t, const std::string& s);
  125. inline Node(World& world);
  126. inline Node(World& world, const SordNode* node);
  127. inline Node(World& world, SordNode* node, bool copy=false);
  128. inline Node(const Node& other);
  129. inline ~Node();
  130. inline Type type() const {
  131. return _c_obj ? (Type)sord_node_get_type(_c_obj) : UNKNOWN;
  132. }
  133. inline const SordNode* get_node() const { return _c_obj; }
  134. inline SordNode* get_node() { return _c_obj; }
  135. const SerdNode* to_serd_node() {
  136. return sord_node_to_serd_node(_c_obj);
  137. }
  138. inline bool is_valid() const { return type() != UNKNOWN; }
  139. inline bool operator<(const Node& other) const {
  140. if (type() != other.type()) {
  141. return type() < other.type();
  142. } else {
  143. return to_string() < other.to_string();
  144. }
  145. }
  146. Node& operator=(const Node& other) {
  147. if (&other != this) {
  148. if (_c_obj) {
  149. sord_node_free(_world->c_obj(), _c_obj);
  150. }
  151. _world = other._world;
  152. _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL;
  153. }
  154. return *this;
  155. }
  156. inline bool operator==(const Node& other) const {
  157. return sord_node_equals(_c_obj, other._c_obj);
  158. }
  159. inline const uint8_t* to_u_string() const;
  160. inline const char* to_c_string() const;
  161. inline std::string to_string() const;
  162. inline bool is_literal_type(const char* type_uri) const;
  163. inline bool is_uri() const { return _c_obj && type() == URI; }
  164. inline bool is_blank() const { return _c_obj && type() == BLANK; }
  165. inline bool is_int() const { return is_literal_type(SORD_NS_XSD "integer"); }
  166. inline bool is_float() const { return is_literal_type(SORD_NS_XSD "decimal"); }
  167. inline bool is_bool() const { return is_literal_type(SORD_NS_XSD "boolean"); }
  168. inline int to_int() const;
  169. inline float to_float() const;
  170. inline bool to_bool() const;
  171. inline static Node blank_id(World& world, const std::string base="b") {
  172. const uint64_t num = world.blank_id();
  173. std::ostringstream ss;
  174. ss << base << num;
  175. return Node(world, Node::BLANK, ss.str());
  176. }
  177. private:
  178. World* _world;
  179. };
  180. inline std::ostream&
  181. operator<<(std::ostream& os, const Node& node)
  182. {
  183. return os << node.to_string();
  184. }
  185. class URI : public Node {
  186. public:
  187. inline URI(World& world, const std::string& s)
  188. : Node(world, Node::URI, s) {}
  189. inline URI(World& world, const std::string& s, const std::string& base)
  190. : Node(world, sord_new_relative_uri(world.world(),
  191. (const uint8_t*)s.c_str(),
  192. (const uint8_t*)base.c_str()))
  193. {}
  194. };
  195. class Curie : public Node {
  196. public:
  197. inline Curie(World& world, const std::string& s)
  198. : Node(world, Node::URI, world.prefixes().expand(s)) {}
  199. };
  200. class Literal : public Node {
  201. public:
  202. inline Literal(World& world, const std::string& s)
  203. : Node(world, Node::LITERAL, s) {}
  204. static inline Node decimal(World& world, double d, unsigned frac_digits) {
  205. const SerdNode val = serd_node_new_decimal(d, 7);
  206. const SerdNode type = serd_node_from_string(
  207. SERD_URI, (const uint8_t*)SORD_NS_XSD "decimal");
  208. return Node(
  209. world,
  210. sord_node_from_serd_node(
  211. world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL),
  212. false);
  213. }
  214. static inline Node integer(World& world, int64_t i) {
  215. const SerdNode val = serd_node_new_integer(i);
  216. const SerdNode type = serd_node_from_string(
  217. SERD_URI, (const uint8_t*)SORD_NS_XSD "integer");
  218. return Node(
  219. world,
  220. sord_node_from_serd_node(
  221. world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL),
  222. false);
  223. }
  224. };
  225. inline
  226. Node::Node(World& world, Type type, const std::string& s)
  227. : _world(&world)
  228. {
  229. switch (type) {
  230. case URI:
  231. _c_obj = sord_new_uri(
  232. world.world(), (const unsigned char*)s.c_str());
  233. break;
  234. case LITERAL:
  235. _c_obj = sord_new_literal(
  236. world.world(), NULL, (const unsigned char*)s.c_str(), NULL);
  237. break;
  238. case BLANK:
  239. _c_obj = sord_new_blank(
  240. world.world(), (const unsigned char*)s.c_str());
  241. break;
  242. default:
  243. _c_obj = NULL;
  244. }
  245. assert(this->type() == type);
  246. }
  247. inline
  248. Node::Node(World& world)
  249. : _world(&world)
  250. {
  251. Node me = blank_id(world);
  252. *this = me;
  253. }
  254. inline
  255. Node::Node(World& world, const SordNode* node)
  256. : _world(&world)
  257. {
  258. _c_obj = sord_node_copy(node);
  259. }
  260. inline
  261. Node::Node(World& world, SordNode* node, bool copy)
  262. : _world(&world)
  263. {
  264. _c_obj = copy ? sord_node_copy(node) : node;
  265. }
  266. inline
  267. Node::Node(const Node& other)
  268. : Wrapper<SordNode*>()
  269. , _world(other._world)
  270. {
  271. if (_world) {
  272. _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL;
  273. }
  274. assert((!_c_obj && !other._c_obj) || to_string() == other.to_string());
  275. }
  276. inline
  277. Node::~Node()
  278. {
  279. if (_world) {
  280. sord_node_free(_world->c_obj(), _c_obj);
  281. }
  282. }
  283. inline std::string
  284. Node::to_string() const
  285. {
  286. return _c_obj ? (const char*)sord_node_get_string(_c_obj) : "";
  287. }
  288. inline const char*
  289. Node::to_c_string() const
  290. {
  291. return (const char*)sord_node_get_string(_c_obj);
  292. }
  293. inline const uint8_t*
  294. Node::to_u_string() const
  295. {
  296. return sord_node_get_string(_c_obj);
  297. }
  298. inline bool
  299. Node::is_literal_type(const char* type_uri) const
  300. {
  301. if (_c_obj && sord_node_get_type(_c_obj) == SORD_LITERAL) {
  302. const SordNode* datatype = sord_node_get_datatype(_c_obj);
  303. if (datatype && !strcmp((const char*)sord_node_get_string(datatype),
  304. type_uri))
  305. return true;
  306. }
  307. return false;
  308. }
  309. inline int
  310. Node::to_int() const
  311. {
  312. assert(is_int());
  313. char* endptr;
  314. return strtol((const char*)sord_node_get_string(_c_obj), &endptr, 10);
  315. }
  316. inline float
  317. Node::to_float() const
  318. {
  319. assert(is_float());
  320. char* endptr;
  321. return serd_strtod((const char*)sord_node_get_string(_c_obj), &endptr);
  322. }
  323. inline bool
  324. Node::to_bool() const
  325. {
  326. assert(is_bool());
  327. return !strcmp((const char*)sord_node_get_string(_c_obj), "true");
  328. }
  329. struct Iter : public Wrapper<SordIter*> {
  330. inline Iter(World& world, SordIter* c_obj)
  331. : Wrapper<SordIter*>(c_obj), _world(world) {}
  332. inline ~Iter() { sord_iter_free(_c_obj); }
  333. inline bool end() const { return sord_iter_end(_c_obj); }
  334. inline bool next() const { return sord_iter_next(_c_obj); }
  335. inline Iter& operator++() {
  336. assert(!end());
  337. next();
  338. return *this;
  339. }
  340. inline const Node get_subject() const {
  341. SordQuad quad;
  342. sord_iter_get(_c_obj, quad);
  343. return Node(_world, quad[SORD_SUBJECT]);
  344. }
  345. inline const Node get_predicate() const {
  346. SordQuad quad;
  347. sord_iter_get(_c_obj, quad);
  348. return Node(_world, quad[SORD_PREDICATE]);
  349. }
  350. inline const Node get_object() const {
  351. SordQuad quad;
  352. sord_iter_get(_c_obj, quad);
  353. return Node(_world, quad[SORD_OBJECT]);
  354. }
  355. World& _world;
  356. };
  357. /** An RDF Model (collection of triples).
  358. */
  359. class Model : public Noncopyable, public Wrapper<SordModel*> {
  360. public:
  361. inline Model(World& world,
  362. const std::string& base_uri,
  363. unsigned indices = (SORD_SPO | SORD_OPS),
  364. bool graphs = true);
  365. inline ~Model();
  366. inline const Node& base_uri() const { return _base; }
  367. size_t num_quads() const { return sord_num_quads(_c_obj); }
  368. inline void load_file(SerdEnv* env,
  369. SerdSyntax syntax,
  370. const std::string& uri,
  371. const std::string& base_uri="");
  372. inline void load_string(SerdEnv* env,
  373. SerdSyntax syntax,
  374. const char* str,
  375. size_t len,
  376. const std::string& base_uri);
  377. inline SerdStatus write_to_file(
  378. const std::string& uri,
  379. SerdSyntax syntax = SERD_TURTLE,
  380. SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED
  381. |SERD_STYLE_CURIED
  382. |SERD_STYLE_RESOLVED));
  383. inline std::string write_to_string(
  384. const std::string& base_uri,
  385. SerdSyntax syntax = SERD_TURTLE,
  386. SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED
  387. |SERD_STYLE_CURIED
  388. |SERD_STYLE_RESOLVED));
  389. inline void add_statement(const Node& subject,
  390. const Node& predicate,
  391. const Node& object);
  392. inline Iter find(const Node& subject,
  393. const Node& predicate,
  394. const Node& object);
  395. inline Node get(const Node& subject,
  396. const Node& predicate,
  397. const Node& object);
  398. inline World& world() const { return _world; }
  399. private:
  400. World& _world;
  401. Node _base;
  402. SerdWriter* _writer;
  403. size_t _next_blank_id;
  404. };
  405. /** Create an empty in-memory RDF model.
  406. */
  407. inline
  408. Model::Model(World& world,
  409. const std::string& base_uri,
  410. unsigned indices,
  411. bool graphs)
  412. : _world(world)
  413. , _base(world, Node::URI, base_uri)
  414. , _writer(NULL)
  415. {
  416. _c_obj = sord_new(_world.world(), indices, graphs);
  417. }
  418. inline void
  419. Model::load_string(SerdEnv* env,
  420. SerdSyntax syntax,
  421. const char* str,
  422. size_t len,
  423. const std::string& base_uri)
  424. {
  425. SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL);
  426. serd_reader_read_string(reader, (const uint8_t*)str);
  427. serd_reader_free(reader);
  428. }
  429. inline Model::~Model()
  430. {
  431. sord_free(_c_obj);
  432. }
  433. inline void
  434. Model::load_file(SerdEnv* env,
  435. SerdSyntax syntax,
  436. const std::string& data_uri,
  437. const std::string& base_uri)
  438. {
  439. uint8_t* path = serd_file_uri_parse((const uint8_t*)data_uri.c_str(), NULL);
  440. if (!path) {
  441. fprintf(stderr, "Failed to parse file URI <%s>\n", data_uri.c_str());
  442. return;
  443. }
  444. // FIXME: blank prefix parameter?
  445. SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL);
  446. serd_reader_read_file(reader, path);
  447. serd_reader_free(reader);
  448. free(path);
  449. }
  450. inline SerdStatus
  451. Model::write_to_file(const std::string& uri, SerdSyntax syntax, SerdStyle style)
  452. {
  453. uint8_t* path = serd_file_uri_parse((const uint8_t*)uri.c_str(), NULL);
  454. if (!path) {
  455. fprintf(stderr, "Failed to parse file URI <%s>\n", uri.c_str());
  456. return SERD_ERR_BAD_ARG;
  457. }
  458. FILE* const fd = fopen((const char*)path, "w");
  459. if (!fd) {
  460. fprintf(stderr, "Failed to open file %s\n", path);
  461. free(path);
  462. return SERD_ERR_UNKNOWN;
  463. }
  464. free(path);
  465. SerdURI base_uri = SERD_URI_NULL;
  466. if (serd_uri_parse((const uint8_t*)uri.c_str(), &base_uri)) {
  467. fprintf(stderr, "Invalid base URI <%s>\n", uri.c_str());
  468. fclose(fd);
  469. return SERD_ERR_BAD_ARG;
  470. }
  471. SerdWriter* writer = serd_writer_new(syntax,
  472. style,
  473. _world.prefixes().c_obj(),
  474. &base_uri,
  475. serd_file_sink,
  476. fd);
  477. serd_env_foreach(_world.prefixes().c_obj(),
  478. (SerdPrefixSink)serd_writer_set_prefix,
  479. writer);
  480. sord_write(_c_obj, writer, 0);
  481. serd_writer_free(writer);
  482. fclose(fd);
  483. return SERD_SUCCESS;
  484. }
  485. static size_t
  486. string_sink(const void* buf, size_t len, void* stream)
  487. {
  488. std::string* str = (std::string*)stream;
  489. str->append((const char*)buf, len);
  490. return len;
  491. }
  492. inline std::string
  493. Model::write_to_string(const std::string& base_uri_str,
  494. SerdSyntax syntax,
  495. SerdStyle style)
  496. {
  497. SerdURI base_uri = SERD_URI_NULL;
  498. if (serd_uri_parse((const uint8_t*)base_uri_str.c_str(), &base_uri)) {
  499. fprintf(stderr, "Invalid base URI <%s>\n", base_uri_str.c_str());
  500. return "";
  501. }
  502. std::string ret;
  503. SerdWriter* writer = serd_writer_new(syntax,
  504. style,
  505. _world.prefixes().c_obj(),
  506. &base_uri,
  507. string_sink,
  508. &ret);
  509. serd_env_foreach(_world.prefixes().c_obj(),
  510. (SerdPrefixSink)serd_writer_set_prefix,
  511. writer);
  512. sord_write(_c_obj, writer, 0);
  513. serd_writer_free(writer);
  514. return ret;
  515. }
  516. inline void
  517. Model::add_statement(const Node& subject,
  518. const Node& predicate,
  519. const Node& object)
  520. {
  521. SordQuad quad = { subject.c_obj(),
  522. predicate.c_obj(),
  523. object.c_obj(),
  524. NULL };
  525. sord_add(_c_obj, quad);
  526. }
  527. inline Iter
  528. Model::find(const Node& subject,
  529. const Node& predicate,
  530. const Node& object)
  531. {
  532. SordQuad quad = { subject.c_obj(),
  533. predicate.c_obj(),
  534. object.c_obj(),
  535. NULL };
  536. return Iter(_world, sord_find(_c_obj, quad));
  537. }
  538. inline Node
  539. Model::get(const Node& subject,
  540. const Node& predicate,
  541. const Node& object)
  542. {
  543. SordNode* c_node = sord_get(
  544. _c_obj, subject.c_obj(), predicate.c_obj(), object.c_obj(), NULL);
  545. Node node(_world, c_node);
  546. sord_node_free(_world.c_obj(), c_node);
  547. return node;
  548. }
  549. } // namespace Sord
  550. #endif // SORD_SORDMM_HPP