| @@ -0,0 +1 @@ | |||||
| David Robillard <d@drobilla.net> | |||||
| @@ -0,0 +1,13 @@ | |||||
| Copyright 2011-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| @@ -0,0 +1,66 @@ | |||||
| Installation Instructions | |||||
| ========================= | |||||
| Basic Installation | |||||
| ------------------ | |||||
| Building this software requires only Python. To install with default options: | |||||
| ./waf configure | |||||
| ./waf | |||||
| ./waf install # or sudo ./waf install | |||||
| Configuration Options | |||||
| --------------------- | |||||
| All supported options can be viewed using the command: | |||||
| ./waf --help | |||||
| Most options only need to be passed during the configure stage, for example: | |||||
| ./waf configure --prefix=/usr | |||||
| ./waf | |||||
| ./waf install | |||||
| Compiler Configuration | |||||
| ---------------------- | |||||
| Several standard environment variables can be used to control how compilers are | |||||
| invoked: | |||||
| * CC: Path to C compiler | |||||
| * CFLAGS: C compiler options | |||||
| * CXX: Path to C++ compiler | |||||
| * CXXFLAGS: C++ compiler options | |||||
| * CPPFLAGS: C preprocessor options | |||||
| * LINKFLAGS: Linker options | |||||
| Library Versioning | |||||
| ------------------ | |||||
| This library uses semantic versioning <http://semver.org/>. | |||||
| Several major versions can be installed in parallel. The shared library name, | |||||
| include directory, and pkg-config file are suffixed with the major version | |||||
| number. For example, a library named "foo" at version 1.x.y might install: | |||||
| /usr/include/foo-1/foo/foo.h | |||||
| /usr/lib/foo-1.so.1.x.y | |||||
| /usr/lib/pkgconfig/foo-1.pc | |||||
| Dependencies can check for the package "foo-1" with pkg-config. | |||||
| Packaging | |||||
| --------- | |||||
| Everything can be installed to a specific root directory by passing a --destdir | |||||
| option to the install stage (or setting the DESTDIR environment variable), | |||||
| which adds a prefix to all install paths. For example: | |||||
| ./waf configure --prefix=/usr | |||||
| ./waf | |||||
| ./waf install --destdir=/tmp/package | |||||
| Packages should allow parallel installation of several major versions. For | |||||
| example, the above would be packaged as "foo-1". | |||||
| @@ -0,0 +1,107 @@ | |||||
| sord (0.14.0) stable; | |||||
| * Reduce memory usage and increase performance with a better data structure | |||||
| * Add sord_erase() for erasing statements via an iterator | |||||
| * Fix bugs with stores that contain both graphs and default graph statements | |||||
| * Fix crash caused by multiple deletion of datatype nodes | |||||
| * Fix compilation on compilers that do not support -pthread flag | |||||
| * Fix minor memory leak in sordi | |||||
| * Fix using sordi with stdin | |||||
| * Show sordi errors in standard format | |||||
| * sord_validate: More extensive validation, including cardinality, | |||||
| PlainLiteral, and someValuesFrom restrictions. | |||||
| * This release does not break the ABI, but the semantics of iterators has | |||||
| changed: any modification to a model invalidates iterators on that model | |||||
| * Improve test coverage | |||||
| * Upgrade to waf 1.8.14 | |||||
| -- David Robillard <d@drobilla.net> Thu, 08 Oct 2015 15:37:36 -0400 | |||||
| sord (0.12.2) stable; | |||||
| * Fix iteration over an entire graph (* * * graph) | |||||
| * sordmm.hpp: Remove unused members | |||||
| * Update to waf 1.7.16 | |||||
| -- David Robillard <d@drobilla.net> Fri, 08 Aug 2014 18:03:02 -0400 | |||||
| sord (0.12.0) stable; | |||||
| * Update to waf 1.7.9 and autowaf r90 (install docs to versioned directory) | |||||
| * Add sord_get() for easily getting single property values | |||||
| * sord_validate: Pass type check when range is xsd:anyURI and value is a URI | |||||
| * sord_validate: Support any subClassOf rdf:Property, not just baked-in ones | |||||
| * sordmm.hpp: Add convenient constructors for decimal and integer literals | |||||
| * sordmm.hpp: Add Node::to_serd_node() | |||||
| * sordmm.hpp: Don't automatically add RDF namespace prefix to world | |||||
| -- David Robillard <d@drobilla.net> Mon, 18 Feb 2013 11:05:59 -0500 | |||||
| sord (0.10.4) stable; | |||||
| * Implement better data type validation in sord_validate conformant with | |||||
| the XSD and OWL specifications | |||||
| * Fix memory leaks in sord_validate | |||||
| * Install sord_validate man page | |||||
| * Disable timestamps in HTML documentation for reproducible build | |||||
| -- David Robillard <d@drobilla.net> Sun, 14 Oct 2012 18:23:55 -0400 | |||||
| sord (0.10.0) stable; | |||||
| * Add error callback to world for custom error reporting | |||||
| * Performance and space (per node) improvements | |||||
| * SSE4.2 accelerated hashing for node interning, where available | |||||
| * Make all 'zix' symbols private to avoid symbol clashes in static builds | |||||
| * Remove problematic "Loaded n statements" output from serdi | |||||
| * Strip down API documentation to a single clean page | |||||
| * Fix various hyper-strict warnings | |||||
| * Do not require a C++ compiler to build | |||||
| * Add option to build utilities as static binaries | |||||
| * Upgrade to waf 1.7.2 | |||||
| * sordmm.hpp: Add indices and graphs parameters to Model constructor | |||||
| * sordmm.hpp: Remove overzealous URI scheme assertion | |||||
| * sordmm.hpp: Correctly handle Sord::Node self-assignment | |||||
| -- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 00:19:51 -0400 | |||||
| sord (0.8.0) stable; | |||||
| * Use path variables in pkgconfig files | |||||
| * Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||||
| * Tolerate serd passing NULL nodes to reader callback (serd 0.6.0) | |||||
| * Fix comparison of typed literals | |||||
| * Take advantage of interning in sord_node_equals() | |||||
| * Support compilation as C++ under MSVC++. | |||||
| * Add sord_iter_get_node() | |||||
| * Refuse to intern relative URIs in sord_new_uri*() | |||||
| * Add sord_new_relative_uri() | |||||
| * Add SordInserter for writing to a model via Serd sink functions. | |||||
| * Add convenient sord_search(), sord_ask(), and sord_count() | |||||
| * Add sord_validate tool for validating data against RDF/OWL schemas | |||||
| -- David Robillard <d@drobilla.net> Tue, 17 Apr 2012 18:24:53 -0400 | |||||
| sord (0.5.0) stable; | |||||
| * Remove glib dependency | |||||
| * Add function sord_contains for checking for a triple pattern | |||||
| * Add function sord_write_iter for writing a queried range | |||||
| * Fix Sord::Namespaces::qualify to no longer chop prefixes | |||||
| * Add ability to build static library | |||||
| -- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||||
| sord (0.4.2) stable; | |||||
| * Fix compilation issues on some systems | |||||
| * Fix build system Python 3 compatibility | |||||
| * Bump Serd dependency to 0.4.0 | |||||
| -- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||||
| sord (0.4.0) stable; | |||||
| * Initial release | |||||
| -- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 | |||||
| @@ -0,0 +1,9 @@ | |||||
| Sord | |||||
| ==== | |||||
| Sord is a lightweight C library for storing RDF statements in memory. | |||||
| For more information, see <http://drobilla.net/software/sord>. | |||||
| -- David Robillard <d@drobilla.net> | |||||
| @@ -0,0 +1,187 @@ | |||||
| <doxygenlayout version="1.0"> | |||||
| <!-- Navigation index tabs for HTML output --> | |||||
| <navindex> | |||||
| <tab type="mainpage" visible="yes" title=""/> | |||||
| <tab type="pages" visible="yes" title="" intro=""/> | |||||
| <tab type="modules" visible="yes" title="" intro=""/> | |||||
| <tab type="namespaces" visible="yes" title=""> | |||||
| <tab type="namespacelist" visible="yes" title="" intro=""/> | |||||
| <tab type="namespacemembers" visible="yes" title="" intro=""/> | |||||
| </tab> | |||||
| <tab type="classes" visible="yes" title=""> | |||||
| <tab type="classlist" visible="yes" title="" intro=""/> | |||||
| <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/> | |||||
| <tab type="hierarchy" visible="yes" title="" intro=""/> | |||||
| <tab type="classmembers" visible="yes" title="" intro=""/> | |||||
| </tab> | |||||
| <tab type="files" visible="yes" title=""> | |||||
| <tab type="filelist" visible="yes" title="" intro=""/> | |||||
| <tab type="globals" visible="yes" title="" intro=""/> | |||||
| </tab> | |||||
| <tab type="examples" visible="yes" title="" intro=""/> | |||||
| </navindex> | |||||
| <!-- Layout definition for a class page --> | |||||
| <class> | |||||
| <briefdescription visible="yes"/> | |||||
| <includes visible="$SHOW_INCLUDE_FILES"/> | |||||
| <inheritancegraph visible="$CLASS_GRAPH"/> | |||||
| <collaborationgraph visible="$COLLABORATION_GRAPH"/> | |||||
| <allmemberslink visible="yes"/> | |||||
| <memberdecl> | |||||
| <nestedclasses visible="yes" title=""/> | |||||
| <publictypes title=""/> | |||||
| <publicslots title=""/> | |||||
| <signals title=""/> | |||||
| <publicmethods title=""/> | |||||
| <publicstaticmethods title=""/> | |||||
| <publicattributes title=""/> | |||||
| <publicstaticattributes title=""/> | |||||
| <protectedtypes title=""/> | |||||
| <protectedslots title=""/> | |||||
| <protectedmethods title=""/> | |||||
| <protectedstaticmethods title=""/> | |||||
| <protectedattributes title=""/> | |||||
| <protectedstaticattributes title=""/> | |||||
| <packagetypes title=""/> | |||||
| <packagemethods title=""/> | |||||
| <packagestaticmethods title=""/> | |||||
| <packageattributes title=""/> | |||||
| <packagestaticattributes title=""/> | |||||
| <properties title=""/> | |||||
| <events title=""/> | |||||
| <privatetypes title=""/> | |||||
| <privateslots title=""/> | |||||
| <privatemethods title=""/> | |||||
| <privatestaticmethods title=""/> | |||||
| <privateattributes title=""/> | |||||
| <privatestaticattributes title=""/> | |||||
| <friends title=""/> | |||||
| <related title="" subtitle=""/> | |||||
| <membergroups visible="yes"/> | |||||
| </memberdecl> | |||||
| <detaileddescription title=""/> | |||||
| <memberdef> | |||||
| <inlineclasses title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <constructors title=""/> | |||||
| <functions title=""/> | |||||
| <related title=""/> | |||||
| <variables title=""/> | |||||
| <properties title=""/> | |||||
| <events title=""/> | |||||
| </memberdef> | |||||
| <usedfiles visible="$SHOW_USED_FILES"/> | |||||
| <authorsection visible="yes"/> | |||||
| </class> | |||||
| <!-- Layout definition for a namespace page --> | |||||
| <namespace> | |||||
| <briefdescription visible="yes"/> | |||||
| <memberdecl> | |||||
| <nestednamespaces visible="yes" title=""/> | |||||
| <classes visible="yes" title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <functions title=""/> | |||||
| <variables title=""/> | |||||
| <membergroups visible="yes"/> | |||||
| </memberdecl> | |||||
| <detaileddescription title=""/> | |||||
| <memberdef> | |||||
| <inlineclasses title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <functions title=""/> | |||||
| <variables title=""/> | |||||
| </memberdef> | |||||
| <authorsection visible="yes"/> | |||||
| </namespace> | |||||
| <!-- Layout definition for a file page --> | |||||
| <file> | |||||
| <briefdescription visible="yes"/> | |||||
| <includes visible="$SHOW_INCLUDE_FILES"/> | |||||
| <includegraph visible="$INCLUDE_GRAPH"/> | |||||
| <includedbygraph visible="$INCLUDED_BY_GRAPH"/> | |||||
| <sourcelink visible="yes"/> | |||||
| <memberdecl> | |||||
| <classes visible="yes" title=""/> | |||||
| <namespaces visible="yes" title=""/> | |||||
| <defines title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <functions title=""/> | |||||
| <variables title=""/> | |||||
| <membergroups visible="yes"/> | |||||
| </memberdecl> | |||||
| <detaileddescription title=""/> | |||||
| <memberdef> | |||||
| <inlineclasses title=""/> | |||||
| <defines title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <functions title=""/> | |||||
| <variables title=""/> | |||||
| </memberdef> | |||||
| <authorsection/> | |||||
| </file> | |||||
| <!-- Layout definition for a group page --> | |||||
| <group> | |||||
| <briefdescription visible="no"/> | |||||
| <groupgraph visible="$GROUP_GRAPHS"/> | |||||
| <detaileddescription title=""/> | |||||
| <memberdecl> | |||||
| <nestedgroups visible="yes" title=""/> | |||||
| <dirs visible="yes" title=""/> | |||||
| <files visible="yes" title=""/> | |||||
| <namespaces visible="yes" title=""/> | |||||
| <classes visible="yes" title=""/> | |||||
| <defines title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <enumvalues title=""/> | |||||
| <functions title=""/> | |||||
| <variables title=""/> | |||||
| <signals title=""/> | |||||
| <publicslots title=""/> | |||||
| <protectedslots title=""/> | |||||
| <privateslots title=""/> | |||||
| <events title=""/> | |||||
| <properties title=""/> | |||||
| <friends title=""/> | |||||
| <membergroups visible="yes"/> | |||||
| </memberdecl> | |||||
| <memberdef> | |||||
| <pagedocs/> | |||||
| <inlineclasses title=""/> | |||||
| <defines title=""/> | |||||
| <typedefs title=""/> | |||||
| <enums title=""/> | |||||
| <enumvalues title=""/> | |||||
| <functions title=""/> | |||||
| <variables title=""/> | |||||
| <signals title=""/> | |||||
| <publicslots title=""/> | |||||
| <protectedslots title=""/> | |||||
| <privateslots title=""/> | |||||
| <events title=""/> | |||||
| <properties title=""/> | |||||
| <friends title=""/> | |||||
| </memberdef> | |||||
| <authorsection visible="yes"/> | |||||
| </group> | |||||
| <!-- Layout definition for a directory page --> | |||||
| <directory> | |||||
| <briefdescription visible="yes"/> | |||||
| <directorygraph visible="yes"/> | |||||
| <memberdecl> | |||||
| <dirs visible="yes"/> | |||||
| <files visible="yes"/> | |||||
| </memberdecl> | |||||
| <detaileddescription title=""/> | |||||
| </directory> | |||||
| </doxygenlayout> | |||||
| @@ -0,0 +1,57 @@ | |||||
| .TH SORD_VALIDATE 1 "21 Mar 2012" | |||||
| .SH NAME | |||||
| .B sord_validate \- Validate RDF data | |||||
| .SH SYNOPSIS | |||||
| sord_validate [OPTION]... INPUT... | |||||
| .SH OPTIONS | |||||
| .TP | |||||
| \fB\-h\fR | |||||
| Print the command line options. | |||||
| .TP | |||||
| \fB\-l\fR | |||||
| Print errors on a single line. | |||||
| .TP | |||||
| \fB\-v\fR | |||||
| Display version information and exit. | |||||
| .SH DESCRIPTION | |||||
| This is a simple validator which checks that all used properties are actually | |||||
| defined, and that the domain and range of properties is explicitly correct. | |||||
| Note that an "error" from this program does not necessarily mean data is | |||||
| invalid, since it is not required to explicitly list types in RDF, however it | |||||
| is a good idea to do so. | |||||
| This program never retrieves data from the web or magical places on the file | |||||
| system, it only processes files passed directly on the command line. This | |||||
| means you must pass all used vocabularies to get a useful result. | |||||
| If an appropriate schema is available, literals are checked against datatype | |||||
| definitions (both the explicit datatype of the literal itself as well as any | |||||
| types implied by the corresponding property). Three XML Schema Datatypes (XSD) | |||||
| constraints are currently supported: regular expressions (xsd:pattern), and | |||||
| inclusive range (xsd:minimumInclusive and xsd:maximumInclusive). Given an | |||||
| appropriate schema, this is enough to validate against most of the standard XSD | |||||
| datatypes. | |||||
| .SH EXAMPLES | |||||
| sord_validate `find ~/schemas/ -name '*.ttl'` data.ttl | |||||
| .SH AUTHOR | |||||
| sord_validate was written by David Robillard <d@drobilla.net> | |||||
| .SH COPYRIGHT | |||||
| Copyright \(co 2012-2013 David Robillard. | |||||
| .br | |||||
| License: <http://www.opensource.org/licenses/isc-license> | |||||
| .br | |||||
| This is free software; you are free to change and redistribute it. | |||||
| .br | |||||
| There is NO WARRANTY, to the extent permitted by law. | |||||
| .SH "SEE ALSO" | |||||
| <http://drobilla.net/software/sord> | |||||
| @@ -0,0 +1,43 @@ | |||||
| .TH SORDI 1 "17 Jan 2012" | |||||
| .SH NAME | |||||
| .B sordi \- Load and re-serialise RDF data | |||||
| .SH SYNOPSIS | |||||
| sordi [OPTION]... INPUT BASE_URI | |||||
| .SH OPTIONS | |||||
| .TP | |||||
| \fB\-h\fR | |||||
| Print the command line options. | |||||
| .TP | |||||
| \fB\-i SYNTAX\fR | |||||
| Read input in SYNTAX (`turtle' or `ntriples'). | |||||
| .TP | |||||
| \fB\-o SYNTAX\fR | |||||
| Write output in SYNTAX (`turtle' or `ntriples'). | |||||
| .TP | |||||
| \fB\-s INPUT\fR | |||||
| Parse INPUT as a string (terminates options). | |||||
| .TP | |||||
| \fB\-v\fR | |||||
| Display version information and exit. | |||||
| .SH AUTHOR | |||||
| Sordi was written by David Robillard <d@drobilla.net> | |||||
| .SH COPYRIGHT | |||||
| Copyright \(co 2011-2013 David Robillard. | |||||
| .br | |||||
| License: <http://www.opensource.org/licenses/isc-license> | |||||
| .br | |||||
| This is free software; you are free to change and redistribute it. | |||||
| .br | |||||
| There is NO WARRANTY, to the extent permitted by law. | |||||
| .SH "SEE ALSO" | |||||
| <http://drobilla.net/software/sord> | |||||
| @@ -0,0 +1,563 @@ | |||||
| body { | |||||
| font-size: medium; | |||||
| font-family: sans-serif; | |||||
| } | |||||
| #top { | |||||
| background-color: #F3F3F3; | |||||
| margin: 0; | |||||
| padding: 0; | |||||
| border-bottom: 1px solid #DDD; | |||||
| margin-bottom: 1ex; | |||||
| font-size: xx-large; | |||||
| font-weight: bold; | |||||
| } | |||||
| div.header { | |||||
| display: none; | |||||
| } | |||||
| .tabs { | |||||
| display: none; | |||||
| } | |||||
| h1 h2 h3 h4 h5 h6 { | |||||
| font-weight: bold; | |||||
| } | |||||
| h1 { | |||||
| font-size: 164%; | |||||
| } | |||||
| h2 { | |||||
| font-size: 132%; | |||||
| } | |||||
| h3 { | |||||
| font-size: 124%; | |||||
| } | |||||
| h4 { | |||||
| font-size: 116%; | |||||
| } | |||||
| h5 { | |||||
| font-size: 108%; | |||||
| } | |||||
| h6 { | |||||
| font-size: 100%; | |||||
| } | |||||
| p { | |||||
| margin: 0 0 1ex 0; | |||||
| } | |||||
| br { | |||||
| display: none; | |||||
| } | |||||
| dt { | |||||
| font-weight: 700; | |||||
| } | |||||
| div.multicol { | |||||
| } | |||||
| p.startli,p.startdd,p.starttd { | |||||
| margin-top: 2px; | |||||
| } | |||||
| p.endli { | |||||
| margin-bottom: 0; | |||||
| } | |||||
| p.enddd { | |||||
| margin-bottom: 4px; | |||||
| } | |||||
| p.endtd { | |||||
| margin-bottom: 2px; | |||||
| } | |||||
| caption { | |||||
| font-weight: 700; | |||||
| } | |||||
| span.legend { | |||||
| font-size: 70%; | |||||
| text-align: center; | |||||
| } | |||||
| h3.version { | |||||
| font-size: 90%; | |||||
| text-align: center; | |||||
| } | |||||
| div.qindex,div.navtab { | |||||
| background-color: #EBEFF6; | |||||
| border: 1px solid #A3B4D7; | |||||
| text-align: center; | |||||
| margin: 2px; | |||||
| padding: 2px; | |||||
| } | |||||
| div.qindex,div.navpath { | |||||
| width: 100%; | |||||
| line-height: 140%; | |||||
| } | |||||
| div.navtab { | |||||
| margin-right: 15px; | |||||
| } | |||||
| /* @group Link Styling */ | |||||
| a { | |||||
| color: #3D8C57; | |||||
| text-decoration: none; | |||||
| } | |||||
| .contents a:visited { | |||||
| color: #50755E; | |||||
| } | |||||
| a:hover { | |||||
| text-decoration: underline; | |||||
| } | |||||
| a.qindexHL { | |||||
| background-color: #9CAFD4; | |||||
| color: #FFF; | |||||
| border: 1px double #869DCA; | |||||
| } | |||||
| a.code { | |||||
| color: #4665A2; | |||||
| } | |||||
| a.codeRef { | |||||
| color: #4665A2; | |||||
| } | |||||
| /* @end */ | |||||
| dl.el { | |||||
| margin-left: -1cm; | |||||
| } | |||||
| .fragment { | |||||
| font-family: monospace, fixed; | |||||
| font-size: 105%; | |||||
| } | |||||
| pre.fragment { | |||||
| border: 1px solid #C4C4C4; | |||||
| background-color: #F9F9F9; | |||||
| padding: 4px 6px; | |||||
| margin: 4px 8px 4px 2px; | |||||
| overflow: auto; | |||||
| font-size: 9pt; | |||||
| line-height: 125%; | |||||
| } | |||||
| div.ah { | |||||
| background-color: #000; | |||||
| font-weight: 700; | |||||
| color: #FFF; | |||||
| margin-bottom: 3px; | |||||
| margin-top: 3px; | |||||
| padding: .2em; | |||||
| border: thin solid #333; | |||||
| } | |||||
| div.groupHeader { | |||||
| margin-left: 16px; | |||||
| margin-top: 12px; | |||||
| margin-bottom: 6px; | |||||
| font-weight: 700; | |||||
| } | |||||
| div.groupText { | |||||
| margin-left: 16px; | |||||
| font-style: italic; | |||||
| } | |||||
| body { | |||||
| background: #FFF; | |||||
| color: #000; | |||||
| margin: 0; | |||||
| } | |||||
| div.contents { | |||||
| margin-top: 10px; | |||||
| margin-left: 10px; | |||||
| margin-right: 10px; | |||||
| } | |||||
| td.indexkey { | |||||
| background-color: #EBEFF6; | |||||
| font-weight: 700; | |||||
| border: 1px solid #C4CFE5; | |||||
| margin: 2px 0; | |||||
| padding: 2px 10px; | |||||
| } | |||||
| td.indexvalue { | |||||
| background-color: #EBEFF6; | |||||
| border: 1px solid #C4CFE5; | |||||
| padding: 2px 10px; | |||||
| margin: 2px 0; | |||||
| } | |||||
| tr.memlist { | |||||
| background-color: #EEF1F7; | |||||
| } | |||||
| p.formulaDsp { | |||||
| text-align: center; | |||||
| } | |||||
| img.formulaDsp { | |||||
| } | |||||
| img.formulaInl { | |||||
| vertical-align: middle; | |||||
| } | |||||
| div.center { | |||||
| text-align: center; | |||||
| margin-top: 0; | |||||
| margin-bottom: 0; | |||||
| padding: 0; | |||||
| } | |||||
| div.center img { | |||||
| border: 0; | |||||
| } | |||||
| address.footer { | |||||
| text-align: right; | |||||
| padding: 0 0.25em 0.25em 0; | |||||
| } | |||||
| img.footer { | |||||
| border: 0; | |||||
| vertical-align: middle; | |||||
| } | |||||
| /* @group Code Colorization */ | |||||
| span.keyword { | |||||
| color: green; | |||||
| } | |||||
| span.keywordtype { | |||||
| color: #604020; | |||||
| } | |||||
| span.keywordflow { | |||||
| color: #e08000; | |||||
| } | |||||
| span.comment { | |||||
| color: maroon; | |||||
| } | |||||
| span.preprocessor { | |||||
| color: #806020; | |||||
| } | |||||
| span.stringliteral { | |||||
| color: #002080; | |||||
| } | |||||
| span.charliteral { | |||||
| color: teal; | |||||
| } | |||||
| span.vhdldigit { | |||||
| color: #F0F; | |||||
| } | |||||
| span.vhdlkeyword { | |||||
| color: #700070; | |||||
| } | |||||
| span.vhdllogic { | |||||
| color: red; | |||||
| } | |||||
| /* @end */ | |||||
| td.tiny { | |||||
| font-size: 75%; | |||||
| } | |||||
| .dirtab { | |||||
| padding: 4px; | |||||
| border-collapse: collapse; | |||||
| border: 1px solid #A3B4D7; | |||||
| } | |||||
| th.dirtab { | |||||
| background: #EBEFF6; | |||||
| font-weight: 700; | |||||
| } | |||||
| hr { | |||||
| height: 0; | |||||
| border: none; | |||||
| border-top: 1px solid #DDD; | |||||
| margin: 2em 0 1em; | |||||
| } | |||||
| hr.footer { | |||||
| height: 1px; | |||||
| } | |||||
| /* @group Member Descriptions */ | |||||
| table.memberdecls { | |||||
| border-spacing: 0; | |||||
| font-size: small; | |||||
| } | |||||
| .mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||||
| background-color: #FBFBFB; | |||||
| margin: 0; | |||||
| padding: 0.25ex; | |||||
| } | |||||
| .mdescLeft,.mdescRight { | |||||
| color: #555; | |||||
| } | |||||
| .memItemLeft,.memItemRight,.memTemplParams { | |||||
| border-top: 1px solid #DDD; | |||||
| } | |||||
| .memItemLeft,.memTemplItemLeft { | |||||
| white-space: nowrap; | |||||
| padding-left: 2em; | |||||
| } | |||||
| .memTemplParams { | |||||
| color: #464646; | |||||
| white-space: nowrap; | |||||
| } | |||||
| /* @end */ | |||||
| /* @group Member Details */ | |||||
| /* Styles for detailed member documentation */ | |||||
| .memtemplate { | |||||
| font-size: 80%; | |||||
| color: #4665A2; | |||||
| font-weight: bold; | |||||
| } | |||||
| .memnav { | |||||
| background-color: #EBEFF6; | |||||
| border: 1px solid #A3B4D7; | |||||
| text-align: center; | |||||
| margin: 2px; | |||||
| margin-right: 15px; | |||||
| padding: 2px; | |||||
| } | |||||
| .memitem { | |||||
| padding: 0; | |||||
| margin: 1ex 0 2ex 0; | |||||
| border: 1px solid #CCC; | |||||
| } | |||||
| .memname { | |||||
| white-space: nowrap; | |||||
| font-weight: bold; | |||||
| } | |||||
| .memproto { | |||||
| border-bottom: 1px solid #DDD; | |||||
| padding: 0.5ex; | |||||
| font-weight: bold; | |||||
| background-color: #F3F3F3; | |||||
| } | |||||
| .memdoc { | |||||
| padding: 1ex; | |||||
| background-color: #FBFBFB; | |||||
| border-top-width: 0; | |||||
| } | |||||
| .paramkey { | |||||
| text-align: right; | |||||
| } | |||||
| .paramtype { | |||||
| white-space: nowrap; | |||||
| } | |||||
| .paramname { | |||||
| color: #602020; | |||||
| white-space: nowrap; | |||||
| } | |||||
| .paramname em { | |||||
| font-style: normal; | |||||
| } | |||||
| /* @end */ | |||||
| /* @group Directory (tree) */ | |||||
| /* for the tree view */ | |||||
| .ftvtree { | |||||
| font-family: sans-serif; | |||||
| margin: 0; | |||||
| } | |||||
| /* these are for tree view when used as main index */ | |||||
| .directory { | |||||
| font-size: 9pt; | |||||
| font-weight: bold; | |||||
| margin: 5px; | |||||
| } | |||||
| .directory h3 { | |||||
| margin: 0; | |||||
| margin-top: 1em; | |||||
| font-size: 11pt; | |||||
| } | |||||
| .directory > h3 { | |||||
| margin-top: 0; | |||||
| } | |||||
| .directory p { | |||||
| margin: 0; | |||||
| white-space: nowrap; | |||||
| } | |||||
| .directory div { | |||||
| display: none; | |||||
| margin: 0; | |||||
| } | |||||
| .directory img { | |||||
| vertical-align: -30%; | |||||
| } | |||||
| /* these are for tree view when not used as main index */ | |||||
| .directory-alt { | |||||
| font-size: 100%; | |||||
| font-weight: bold; | |||||
| } | |||||
| .directory-alt h3 { | |||||
| margin: 0; | |||||
| margin-top: 1em; | |||||
| font-size: 11pt; | |||||
| } | |||||
| .directory-alt > h3 { | |||||
| margin-top: 0; | |||||
| } | |||||
| .directory-alt p { | |||||
| margin: 0; | |||||
| white-space: nowrap; | |||||
| } | |||||
| .directory-alt div { | |||||
| display: none; | |||||
| margin: 0; | |||||
| } | |||||
| .directory-alt img { | |||||
| vertical-align: -30%; | |||||
| } | |||||
| /* @end */ | |||||
| div.dynheader { | |||||
| margin-top: 8px; | |||||
| } | |||||
| address { | |||||
| font-style: normal; | |||||
| color: #2A3D61; | |||||
| } | |||||
| table.doxtable { | |||||
| border-collapse: collapse; | |||||
| margin: 0.5ex; | |||||
| } | |||||
| table.doxtable td,table.doxtable th { | |||||
| border: 1px solid #DDD; | |||||
| padding: 3px 7px 2px; | |||||
| } | |||||
| table.doxtable th { | |||||
| background-color: #F3F3F3; | |||||
| color: #000; | |||||
| padding-bottom: 4px; | |||||
| padding-top: 5px; | |||||
| text-align: left; | |||||
| font-weight: bold; | |||||
| } | |||||
| .tabsearch { | |||||
| top: 0; | |||||
| left: 10px; | |||||
| height: 36px; | |||||
| z-index: 101; | |||||
| overflow: hidden; | |||||
| font-size: 13px; | |||||
| } | |||||
| .navpath ul { | |||||
| font-size: 11px; | |||||
| height: 30px; | |||||
| line-height: 30px; | |||||
| color: #8AA0CC; | |||||
| border: 1px solid #C2CDE4; | |||||
| overflow: hidden; | |||||
| margin: 0; | |||||
| padding: 0; | |||||
| } | |||||
| .navpath li { | |||||
| list-style-type: none; | |||||
| float: left; | |||||
| padding-left: 10px; | |||||
| padding-right: 15px; | |||||
| color: #364D7C; | |||||
| } | |||||
| .navpath a { | |||||
| height: 32px; | |||||
| display: block; | |||||
| text-decoration: none; | |||||
| outline: none; | |||||
| } | |||||
| .navpath a:hover { | |||||
| color: #6884BD; | |||||
| } | |||||
| div.summary { | |||||
| float: right; | |||||
| font-size: 8pt; | |||||
| padding-right: 5px; | |||||
| width: 50%; | |||||
| text-align: right; | |||||
| } | |||||
| div.summary a { | |||||
| white-space: nowrap; | |||||
| } | |||||
| div.header { | |||||
| background-color: #F3F3F3; | |||||
| margin: 0; | |||||
| border-bottom: 1px solid #DDD; | |||||
| } | |||||
| div.headertitle { | |||||
| padding: 5px 5px 5px 10px; | |||||
| font-size: 180%; | |||||
| font-weight: bold; | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| prefix=@PREFIX@ | |||||
| exec_prefix=@EXEC_PREFIX@ | |||||
| libdir=@LIBDIR@ | |||||
| includedir=@INCLUDEDIR@ | |||||
| Name: Sord | |||||
| Version: @SORD_VERSION@ | |||||
| Description: A lightweight C library for storing RDF statements in memory. | |||||
| Requires: @PKG_serd_0@ | |||||
| Libs: -L${libdir} -l@LIB_SORD@ | |||||
| Cflags: -I${includedir}/sord-@SORD_MAJOR_VERSION@ | |||||
| @@ -0,0 +1,646 @@ | |||||
| /* | |||||
| Copyright 2011-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file sord.h API for Sord, a lightweight RDF model library. | |||||
| */ | |||||
| #ifndef SORD_SORD_H | |||||
| #define SORD_SORD_H | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| #include <stdio.h> | |||||
| #include "serd/serd.h" | |||||
| #ifdef SORD_SHARED | |||||
| # ifdef _WIN32 | |||||
| # define SORD_LIB_IMPORT __declspec(dllimport) | |||||
| # define SORD_LIB_EXPORT __declspec(dllexport) | |||||
| # else | |||||
| # define SORD_LIB_IMPORT __attribute__((visibility("default"))) | |||||
| # define SORD_LIB_EXPORT __attribute__((visibility("default"))) | |||||
| # endif | |||||
| # ifdef SORD_INTERNAL | |||||
| # define SORD_API SORD_LIB_EXPORT | |||||
| # else | |||||
| # define SORD_API SORD_LIB_IMPORT | |||||
| # endif | |||||
| #else | |||||
| # define SORD_API | |||||
| #endif | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| /** | |||||
| @defgroup sord Sord | |||||
| A lightweight RDF model library. | |||||
| Sord stores RDF (subject object predicate context) quads, where the context | |||||
| may be omitted (to represent triples in the default graph). | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Sord World. | |||||
| The World represents all library state, including interned strings. | |||||
| */ | |||||
| typedef struct SordWorldImpl SordWorld; | |||||
| /** | |||||
| Sord Model. | |||||
| A model is an indexed set of Quads (i.e. it can contain several RDF | |||||
| graphs). It may be searched using various patterns depending on which | |||||
| indices are enabled. | |||||
| */ | |||||
| typedef struct SordModelImpl SordModel; | |||||
| /** | |||||
| Model Inserter. | |||||
| An inserter is used for writing statements to a model using the Serd sink | |||||
| interface. This makes it simple to write to a model directly using a | |||||
| SerdReader, or any other code that writes statements to a SerdStatementSink. | |||||
| */ | |||||
| typedef struct SordInserterImpl SordInserter; | |||||
| /** | |||||
| Model Iterator. | |||||
| */ | |||||
| typedef struct SordIterImpl SordIter; | |||||
| /** | |||||
| RDF Node. | |||||
| A Node is a component of a Quad. Nodes may be URIs, blank nodes, or | |||||
| (in the case of quad objects only) string literals. Literal nodes may | |||||
| have an associate language or datatype (but not both). | |||||
| */ | |||||
| typedef struct SordNodeImpl SordNode; | |||||
| /** | |||||
| Quad of nodes (a statement), or a quad pattern. | |||||
| Nodes are ordered (S P O G). The ID of the default graph is 0. | |||||
| */ | |||||
| typedef const SordNode* SordQuad[4]; | |||||
| /** | |||||
| Index into a SordQuad. | |||||
| */ | |||||
| typedef enum { | |||||
| SORD_SUBJECT = 0, /**< Subject */ | |||||
| SORD_PREDICATE = 1, /**< Predicate (a.k.a. "key") */ | |||||
| SORD_OBJECT = 2, /**< Object (a.k.a. "value") */ | |||||
| SORD_GRAPH = 3 /**< Graph (a.k.a. "context") */ | |||||
| } SordQuadIndex; | |||||
| /** | |||||
| Type of a node. | |||||
| */ | |||||
| typedef enum { | |||||
| SORD_URI = 1, /**< URI */ | |||||
| SORD_BLANK = 2, /**< Blank node identifier */ | |||||
| SORD_LITERAL = 3 /**< Literal (string with optional lang or datatype) */ | |||||
| } SordNodeType; | |||||
| /** | |||||
| Indexing option. | |||||
| */ | |||||
| typedef enum { | |||||
| SORD_SPO = 1, /**< Subject, Predicate, Object */ | |||||
| SORD_SOP = 1 << 1, /**< Subject, Object, Predicate */ | |||||
| SORD_OPS = 1 << 2, /**< Object, Predicate, Subject */ | |||||
| SORD_OSP = 1 << 3, /**< Object, Subject, Predicate */ | |||||
| SORD_PSO = 1 << 4, /**< Predicate, Subject, Object */ | |||||
| SORD_POS = 1 << 5 /**< Predicate, Object, Subject */ | |||||
| } SordIndexOption; | |||||
| /** | |||||
| @name World | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Create a new Sord World. | |||||
| It is safe to use multiple worlds in one process, though no data | |||||
| (e.g. nodes) can be shared between worlds, and this should be avoided if | |||||
| possible for performance reasons. | |||||
| */ | |||||
| SORD_API | |||||
| SordWorld* | |||||
| sord_world_new(void); | |||||
| /** | |||||
| Free `world`. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_world_free(SordWorld* world); | |||||
| /** | |||||
| Set a function to be called when errors occur. | |||||
| The `error_sink` will be called with `handle` as its first argument. If | |||||
| no error function is set, errors are printed to stderr. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_world_set_error_sink(SordWorld* world, | |||||
| SerdErrorSink error_sink, | |||||
| void* handle); | |||||
| /** | |||||
| @} | |||||
| @name Node | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Get a URI node from a string. | |||||
| Note this function measures `str`, which is a common bottleneck. | |||||
| Use sord_node_from_serd_node instead if `str` is already measured. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_new_uri(SordWorld* world, const uint8_t* uri); | |||||
| /** | |||||
| Get a URI node from a relative URI string. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_new_relative_uri(SordWorld* world, | |||||
| const uint8_t* str, | |||||
| const uint8_t* base_uri); | |||||
| /** | |||||
| Get a blank node from a string. | |||||
| Note this function measures `str`, which is a common bottleneck. | |||||
| Use sord_node_from_serd_node instead if `str` is already measured. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_new_blank(SordWorld* world, const uint8_t* str); | |||||
| /** | |||||
| Get a literal node from a string. | |||||
| Note this function measures `str`, which is a common bottleneck. | |||||
| Use sord_node_from_serd_node instead if `str` is already measured. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_new_literal(SordWorld* world, | |||||
| SordNode* datatype, | |||||
| const uint8_t* str, | |||||
| const char* lang); | |||||
| /** | |||||
| Copy a node (obtain a reference). | |||||
| Node that since nodes are interned and reference counted, this does not | |||||
| actually create a deep copy of `node`. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_node_copy(const SordNode* node); | |||||
| /** | |||||
| Free a node (drop a reference). | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_node_free(SordWorld* world, SordNode* node); | |||||
| /** | |||||
| Return the type of a node (SORD_URI, SORD_BLANK, or SORD_LITERAL). | |||||
| */ | |||||
| SORD_API | |||||
| SordNodeType | |||||
| sord_node_get_type(const SordNode* node); | |||||
| /** | |||||
| Return the string value of a node. | |||||
| */ | |||||
| SORD_API | |||||
| const uint8_t* | |||||
| sord_node_get_string(const SordNode* node); | |||||
| /** | |||||
| Return the string value of a node, and set `len` to its length. | |||||
| */ | |||||
| SORD_API | |||||
| const uint8_t* | |||||
| sord_node_get_string_counted(const SordNode* node, size_t* len); | |||||
| /** | |||||
| Return the language of a literal node (or NULL). | |||||
| */ | |||||
| SORD_API | |||||
| const char* | |||||
| sord_node_get_language(const SordNode* node); | |||||
| /** | |||||
| Return the datatype URI of a literal node (or NULL). | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_node_get_datatype(const SordNode* node); | |||||
| /** | |||||
| Return the flags (string attributes) of a node. | |||||
| */ | |||||
| SORD_API | |||||
| SerdNodeFlags | |||||
| sord_node_get_flags(const SordNode* node); | |||||
| /** | |||||
| Return true iff node can be serialised as an inline object. | |||||
| More specifically, this returns true iff the node is the object field | |||||
| of exactly one statement, and therefore can be inlined since it needn't | |||||
| be referred to by name. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_node_is_inline_object(const SordNode* node); | |||||
| /** | |||||
| Return true iff `a` is equal to `b`. | |||||
| Note this is much faster than comparing the node's strings. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_node_equals(const SordNode* a, | |||||
| const SordNode* b); | |||||
| /** | |||||
| Return a SordNode as a SerdNode. | |||||
| The returned node is shared and must not be freed or modified. | |||||
| */ | |||||
| SORD_API | |||||
| const SerdNode* | |||||
| sord_node_to_serd_node(const SordNode* node); | |||||
| /** | |||||
| Create a new SordNode from a SerdNode. | |||||
| The returned node must be freed using sord_node_free. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_node_from_serd_node(SordWorld* world, | |||||
| SerdEnv* env, | |||||
| const SerdNode* node, | |||||
| const SerdNode* datatype, | |||||
| const SerdNode* lang); | |||||
| /** | |||||
| @} | |||||
| @name Model | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Create a new model. | |||||
| @param world The world in which to make this model. | |||||
| @param indices SordIndexOption flags (e.g. SORD_SPO|SORD_OPS). Be sure to | |||||
| enable an index where the most significant node(s) are not variables in your | |||||
| queries (e.g. to make (? P O) queries, enable either SORD_OPS or SORD_POS). | |||||
| @param graphs If true, store (and index) graph contexts. | |||||
| */ | |||||
| SORD_API | |||||
| SordModel* | |||||
| sord_new(SordWorld* world, | |||||
| unsigned indices, | |||||
| bool graphs); | |||||
| /** | |||||
| Close and free `model`. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_free(SordModel* model); | |||||
| /** | |||||
| Get the world associated with `model`. | |||||
| */ | |||||
| SORD_API | |||||
| SordWorld* | |||||
| sord_get_world(SordModel* model); | |||||
| /** | |||||
| Return the number of nodes stored in `world`. | |||||
| Nodes are included in this count iff they are a part of a quad in `world`. | |||||
| */ | |||||
| SORD_API | |||||
| size_t | |||||
| sord_num_nodes(const SordWorld* world); | |||||
| /** | |||||
| Return the number of quads stored in `model`. | |||||
| */ | |||||
| SORD_API | |||||
| size_t | |||||
| sord_num_quads(const SordModel* model); | |||||
| /** | |||||
| Return an iterator to the start of `model`. | |||||
| */ | |||||
| SORD_API | |||||
| SordIter* | |||||
| sord_begin(const SordModel* model); | |||||
| /** | |||||
| Search for statements by a quad pattern. | |||||
| @return an iterator to the first match, or NULL if no matches found. | |||||
| */ | |||||
| SORD_API | |||||
| SordIter* | |||||
| sord_find(SordModel* model, const SordQuad pat); | |||||
| /** | |||||
| Search for statements by nodes. | |||||
| @return an iterator to the first match, or NULL if no matches found. | |||||
| */ | |||||
| SORD_API | |||||
| SordIter* | |||||
| sord_search(SordModel* model, | |||||
| const SordNode* s, | |||||
| const SordNode* p, | |||||
| const SordNode* o, | |||||
| const SordNode* g); | |||||
| /** | |||||
| Search for a single node that matches a pattern. | |||||
| Exactly one of `s`, `p`, `o` must be NULL. | |||||
| This function is mainly useful for predicates that only have one value. | |||||
| The returned node must be freed using sord_node_free. | |||||
| @return the first matching node, or NULL if no matches are found. | |||||
| */ | |||||
| SORD_API | |||||
| SordNode* | |||||
| sord_get(SordModel* model, | |||||
| const SordNode* s, | |||||
| const SordNode* p, | |||||
| const SordNode* o, | |||||
| const SordNode* g); | |||||
| /** | |||||
| Return true iff a statement exists. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_ask(SordModel* model, | |||||
| const SordNode* s, | |||||
| const SordNode* p, | |||||
| const SordNode* o, | |||||
| const SordNode* g); | |||||
| /** | |||||
| Return the number of matching statements. | |||||
| */ | |||||
| SORD_API | |||||
| uint64_t | |||||
| sord_count(SordModel* model, | |||||
| const SordNode* s, | |||||
| const SordNode* p, | |||||
| const SordNode* o, | |||||
| const SordNode* g); | |||||
| /** | |||||
| Check if `model` contains a triple pattern. | |||||
| @return true if `model` contains a match for `pat`, otherwise false. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_contains(SordModel* model, const SordQuad pat); | |||||
| /** | |||||
| Add a quad to a model. | |||||
| Calling this function invalidates all iterators on `model`. | |||||
| @return true on success, false, on error. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_add(SordModel* model, const SordQuad quad); | |||||
| /** | |||||
| Remove a quad from a model. | |||||
| Calling this function invalidates all iterators on `model`. To remove quads | |||||
| while iterating, use sord_erase() instead. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_remove(SordModel* model, const SordQuad quad); | |||||
| /** | |||||
| Remove a quad from a model via an iterator. | |||||
| Calling this function invalidates all iterators on `model` except `iter`. | |||||
| @param iter Iterator to the element to erase, which is incremented to the | |||||
| next value on return. | |||||
| */ | |||||
| SORD_API | |||||
| SerdStatus | |||||
| sord_erase(SordModel* model, SordIter* iter); | |||||
| /** | |||||
| @} | |||||
| @name Inserter | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Create an inserter for writing statements to a model. | |||||
| */ | |||||
| SORD_API | |||||
| SordInserter* | |||||
| sord_inserter_new(SordModel* model, | |||||
| SerdEnv* env); | |||||
| /** | |||||
| Free an inserter. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_inserter_free(SordInserter* inserter); | |||||
| /** | |||||
| Set the current base URI for writing to the model. | |||||
| Note this function can be safely casted to SerdBaseSink. | |||||
| */ | |||||
| SORD_API | |||||
| SerdStatus | |||||
| sord_inserter_set_base_uri(SordInserter* inserter, | |||||
| const SerdNode* uri); | |||||
| /** | |||||
| Set a namespace prefix for writing to the model. | |||||
| Note this function can be safely casted to SerdPrefixSink. | |||||
| */ | |||||
| SORD_API | |||||
| SerdStatus | |||||
| sord_inserter_set_prefix(SordInserter* inserter, | |||||
| const SerdNode* name, | |||||
| const SerdNode* uri); | |||||
| /** | |||||
| Write a statement to the model. | |||||
| Note this function can be safely casted to SerdStatementSink. | |||||
| */ | |||||
| SORD_API | |||||
| SerdStatus | |||||
| sord_inserter_write_statement(SordInserter* inserter, | |||||
| SerdStatementFlags flags, | |||||
| const SerdNode* graph, | |||||
| const SerdNode* subject, | |||||
| const SerdNode* predicate, | |||||
| const SerdNode* object, | |||||
| const SerdNode* object_datatype, | |||||
| const SerdNode* object_lang); | |||||
| /** | |||||
| @} | |||||
| @name Iteration | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Set `quad` to the quad pointed to by `iter`. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_iter_get(const SordIter* iter, SordQuad quad); | |||||
| /** | |||||
| Return a field of the quad pointed to by `iter`. | |||||
| */ | |||||
| SORD_API | |||||
| const SordNode* | |||||
| sord_iter_get_node(const SordIter* iter, SordQuadIndex index); | |||||
| /** | |||||
| Return the store pointed to by `iter`. | |||||
| */ | |||||
| SORD_API | |||||
| const SordModel* | |||||
| sord_iter_get_model(SordIter* iter); | |||||
| /** | |||||
| Increment `iter` to point to the next statement. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_iter_next(SordIter* iter); | |||||
| /** | |||||
| Return true iff `iter` is at the end of its range. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_iter_end(const SordIter* iter); | |||||
| /** | |||||
| Free `iter`. | |||||
| */ | |||||
| SORD_API | |||||
| void | |||||
| sord_iter_free(SordIter* iter); | |||||
| /** | |||||
| @} | |||||
| @name Utilities | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Match two quads (using ID comparison only). | |||||
| This function is a straightforward and fast equivalence match with wildcard | |||||
| support (ID 0 is a wildcard). It does not actually read node data. | |||||
| @return true iff `x` and `y` match. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_quad_match(const SordQuad x, const SordQuad y); | |||||
| /** | |||||
| @} | |||||
| @name Serialisation | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| Return a reader that will read into `model`. | |||||
| */ | |||||
| SORD_API | |||||
| SerdReader* | |||||
| sord_new_reader(SordModel* model, | |||||
| SerdEnv* env, | |||||
| SerdSyntax syntax, | |||||
| SordNode* graph); | |||||
| /** | |||||
| Write a model to a writer. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_write(SordModel* model, | |||||
| SerdWriter* writer, | |||||
| SordNode* graph); | |||||
| /** | |||||
| Write a range to a writer. | |||||
| This increments `iter` to its end, then frees it. | |||||
| */ | |||||
| SORD_API | |||||
| bool | |||||
| sord_write_iter(SordIter* iter, | |||||
| SerdWriter* writer); | |||||
| /** | |||||
| @} | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* SORD_SORD_H */ | |||||
| @@ -0,0 +1,653 @@ | |||||
| /* | |||||
| Copyright 2011-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| /** | |||||
| @file sordmm.hpp | |||||
| Public Sord C++ API. | |||||
| */ | |||||
| #ifndef SORD_SORDMM_HPP | |||||
| #define SORD_SORDMM_HPP | |||||
| #include <cassert> | |||||
| #include <cstring> | |||||
| #include <cstdlib> | |||||
| #include <iostream> | |||||
| #include <set> | |||||
| #include <string> | |||||
| #include <sstream> | |||||
| #include "serd/serd.h" | |||||
| #include "sord/sord.h" | |||||
| #define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#" | |||||
| namespace Sord { | |||||
| /** Utility base class to prevent copying. */ | |||||
| class Noncopyable { | |||||
| protected: | |||||
| Noncopyable() {} | |||||
| ~Noncopyable() {} | |||||
| private: | |||||
| Noncopyable(const Noncopyable&); | |||||
| const Noncopyable& operator=(const Noncopyable&); | |||||
| }; | |||||
| /** C++ wrapper for a Sord object. */ | |||||
| template <typename T> | |||||
| class Wrapper { | |||||
| public: | |||||
| inline Wrapper(T c_obj = NULL) : _c_obj(c_obj) {} | |||||
| inline T c_obj() { return _c_obj; } | |||||
| inline const T c_obj() const { return _c_obj; } | |||||
| protected: | |||||
| T _c_obj; | |||||
| }; | |||||
| /** Collection of RDF namespaces with prefixes. */ | |||||
| class Namespaces : public Wrapper<SerdEnv*> { | |||||
| public: | |||||
| Namespaces() : Wrapper<SerdEnv*>(serd_env_new(NULL)) {} | |||||
| ~Namespaces() { serd_env_free(_c_obj); } | |||||
| static inline SerdNode string_to_node(SerdType type, const std::string& s) { | |||||
| SerdNode ret = { | |||||
| (const uint8_t*)s.c_str(), s.length(), s.length(), 0, type }; | |||||
| return ret; | |||||
| } | |||||
| inline void add(const std::string& name, | |||||
| const std::string& uri) { | |||||
| const SerdNode name_node = string_to_node(SERD_LITERAL, name); | |||||
| const SerdNode uri_node = string_to_node(SERD_URI, uri); | |||||
| serd_env_set_prefix(_c_obj, &name_node, &uri_node); | |||||
| } | |||||
| inline std::string qualify(std::string uri) const { | |||||
| const SerdNode uri_node = string_to_node(SERD_URI, uri); | |||||
| SerdNode prefix; | |||||
| SerdChunk suffix; | |||||
| if (serd_env_qualify(_c_obj, &uri_node, &prefix, &suffix)) { | |||||
| std::string ret((const char*)prefix.buf, prefix.n_bytes); | |||||
| ret.append(":").append((const char*)suffix.buf, suffix.len); | |||||
| return ret; | |||||
| } | |||||
| return uri; | |||||
| } | |||||
| inline std::string expand(const std::string& curie) const { | |||||
| assert(curie.find(":") != std::string::npos); | |||||
| SerdNode curie_node = string_to_node(SERD_CURIE, curie); | |||||
| SerdChunk uri_prefix; | |||||
| SerdChunk uri_suffix; | |||||
| if (!serd_env_expand(_c_obj, &curie_node, &uri_prefix, &uri_suffix)) { | |||||
| std::string ret((const char*)uri_prefix.buf, uri_prefix.len); | |||||
| ret.append((const char*)uri_suffix.buf, uri_suffix.len); | |||||
| return ret; | |||||
| } | |||||
| std::cerr << "CURIE `" << curie << "' has unknown prefix." << std::endl; | |||||
| return curie; | |||||
| } | |||||
| }; | |||||
| /** Sord library state. */ | |||||
| class World : public Noncopyable, public Wrapper<SordWorld*> { | |||||
| public: | |||||
| inline World() | |||||
| : _next_blank_id(0) | |||||
| { | |||||
| _c_obj = sord_world_new(); | |||||
| } | |||||
| inline ~World() { | |||||
| sord_world_free(_c_obj); | |||||
| } | |||||
| inline uint64_t blank_id() { return _next_blank_id++; } | |||||
| inline void add_prefix(const std::string& prefix, const std::string& uri) { | |||||
| _prefixes.add(prefix, uri); | |||||
| } | |||||
| inline const Namespaces& prefixes() const { return _prefixes; } | |||||
| inline SordWorld* world() { return _c_obj; } | |||||
| private: | |||||
| Namespaces _prefixes; | |||||
| std::set<std::string> _blank_ids; | |||||
| uint64_t _next_blank_id; | |||||
| }; | |||||
| /** An RDF Node (resource, literal, etc) | |||||
| */ | |||||
| class Node : public Wrapper<SordNode*> { | |||||
| public: | |||||
| enum Type { | |||||
| UNKNOWN = 0, | |||||
| URI = SORD_URI, | |||||
| BLANK = SORD_BLANK, | |||||
| LITERAL = SORD_LITERAL | |||||
| }; | |||||
| inline Node() : Wrapper<SordNode*>(NULL), _world(NULL) {} | |||||
| inline Node(World& world, Type t, const std::string& s); | |||||
| inline Node(World& world); | |||||
| inline Node(World& world, const SordNode* node); | |||||
| inline Node(World& world, SordNode* node, bool copy=false); | |||||
| inline Node(const Node& other); | |||||
| inline ~Node(); | |||||
| inline Type type() const { | |||||
| return _c_obj ? (Type)sord_node_get_type(_c_obj) : UNKNOWN; | |||||
| } | |||||
| inline const SordNode* get_node() const { return _c_obj; } | |||||
| inline SordNode* get_node() { return _c_obj; } | |||||
| const SerdNode* to_serd_node() { | |||||
| return sord_node_to_serd_node(_c_obj); | |||||
| } | |||||
| inline bool is_valid() const { return type() != UNKNOWN; } | |||||
| inline bool operator<(const Node& other) const { | |||||
| if (type() != other.type()) { | |||||
| return type() < other.type(); | |||||
| } else { | |||||
| return to_string() < other.to_string(); | |||||
| } | |||||
| } | |||||
| Node& operator=(const Node& other) { | |||||
| if (&other != this) { | |||||
| if (_c_obj) { | |||||
| sord_node_free(_world->c_obj(), _c_obj); | |||||
| } | |||||
| _world = other._world; | |||||
| _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL; | |||||
| } | |||||
| return *this; | |||||
| } | |||||
| inline bool operator==(const Node& other) const { | |||||
| return sord_node_equals(_c_obj, other._c_obj); | |||||
| } | |||||
| inline const uint8_t* to_u_string() const; | |||||
| inline const char* to_c_string() const; | |||||
| inline std::string to_string() const; | |||||
| inline bool is_literal_type(const char* type_uri) const; | |||||
| inline bool is_uri() const { return _c_obj && type() == URI; } | |||||
| inline bool is_blank() const { return _c_obj && type() == BLANK; } | |||||
| inline bool is_int() const { return is_literal_type(SORD_NS_XSD "integer"); } | |||||
| inline bool is_float() const { return is_literal_type(SORD_NS_XSD "decimal"); } | |||||
| inline bool is_bool() const { return is_literal_type(SORD_NS_XSD "boolean"); } | |||||
| inline int to_int() const; | |||||
| inline float to_float() const; | |||||
| inline bool to_bool() const; | |||||
| inline static Node blank_id(World& world, const std::string base="b") { | |||||
| const uint64_t num = world.blank_id(); | |||||
| std::ostringstream ss; | |||||
| ss << base << num; | |||||
| return Node(world, Node::BLANK, ss.str()); | |||||
| } | |||||
| private: | |||||
| World* _world; | |||||
| }; | |||||
| inline std::ostream& | |||||
| operator<<(std::ostream& os, const Node& node) | |||||
| { | |||||
| return os << node.to_string(); | |||||
| } | |||||
| class URI : public Node { | |||||
| public: | |||||
| inline URI(World& world, const std::string& s) | |||||
| : Node(world, Node::URI, s) {} | |||||
| inline URI(World& world, const std::string& s, const std::string& base) | |||||
| : Node(world, sord_new_relative_uri(world.world(), | |||||
| (const uint8_t*)s.c_str(), | |||||
| (const uint8_t*)base.c_str())) | |||||
| {} | |||||
| }; | |||||
| class Curie : public Node { | |||||
| public: | |||||
| inline Curie(World& world, const std::string& s) | |||||
| : Node(world, Node::URI, world.prefixes().expand(s)) {} | |||||
| }; | |||||
| class Literal : public Node { | |||||
| public: | |||||
| inline Literal(World& world, const std::string& s) | |||||
| : Node(world, Node::LITERAL, s) {} | |||||
| static inline Node decimal(World& world, double d, unsigned frac_digits) { | |||||
| const SerdNode val = serd_node_new_decimal(d, 7); | |||||
| const SerdNode type = serd_node_from_string( | |||||
| SERD_URI, (const uint8_t*)SORD_NS_XSD "decimal"); | |||||
| return Node( | |||||
| world, | |||||
| sord_node_from_serd_node( | |||||
| world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL), | |||||
| false); | |||||
| } | |||||
| static inline Node integer(World& world, int64_t i) { | |||||
| const SerdNode val = serd_node_new_integer(i); | |||||
| const SerdNode type = serd_node_from_string( | |||||
| SERD_URI, (const uint8_t*)SORD_NS_XSD "integer"); | |||||
| return Node( | |||||
| world, | |||||
| sord_node_from_serd_node( | |||||
| world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL), | |||||
| false); | |||||
| } | |||||
| }; | |||||
| inline | |||||
| Node::Node(World& world, Type type, const std::string& s) | |||||
| : _world(&world) | |||||
| { | |||||
| switch (type) { | |||||
| case URI: | |||||
| _c_obj = sord_new_uri( | |||||
| world.world(), (const unsigned char*)s.c_str()); | |||||
| break; | |||||
| case LITERAL: | |||||
| _c_obj = sord_new_literal( | |||||
| world.world(), NULL, (const unsigned char*)s.c_str(), NULL); | |||||
| break; | |||||
| case BLANK: | |||||
| _c_obj = sord_new_blank( | |||||
| world.world(), (const unsigned char*)s.c_str()); | |||||
| break; | |||||
| default: | |||||
| _c_obj = NULL; | |||||
| } | |||||
| assert(this->type() == type); | |||||
| } | |||||
| inline | |||||
| Node::Node(World& world) | |||||
| : _world(&world) | |||||
| { | |||||
| Node me = blank_id(world); | |||||
| *this = me; | |||||
| } | |||||
| inline | |||||
| Node::Node(World& world, const SordNode* node) | |||||
| : _world(&world) | |||||
| { | |||||
| _c_obj = sord_node_copy(node); | |||||
| } | |||||
| inline | |||||
| Node::Node(World& world, SordNode* node, bool copy) | |||||
| : _world(&world) | |||||
| { | |||||
| _c_obj = copy ? sord_node_copy(node) : node; | |||||
| } | |||||
| inline | |||||
| Node::Node(const Node& other) | |||||
| : Wrapper<SordNode*>() | |||||
| , _world(other._world) | |||||
| { | |||||
| if (_world) { | |||||
| _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL; | |||||
| } | |||||
| assert((!_c_obj && !other._c_obj) || to_string() == other.to_string()); | |||||
| } | |||||
| inline | |||||
| Node::~Node() | |||||
| { | |||||
| if (_world) { | |||||
| sord_node_free(_world->c_obj(), _c_obj); | |||||
| } | |||||
| } | |||||
| inline std::string | |||||
| Node::to_string() const | |||||
| { | |||||
| return _c_obj ? (const char*)sord_node_get_string(_c_obj) : ""; | |||||
| } | |||||
| inline const char* | |||||
| Node::to_c_string() const | |||||
| { | |||||
| return (const char*)sord_node_get_string(_c_obj); | |||||
| } | |||||
| inline const uint8_t* | |||||
| Node::to_u_string() const | |||||
| { | |||||
| return sord_node_get_string(_c_obj); | |||||
| } | |||||
| inline bool | |||||
| Node::is_literal_type(const char* type_uri) const | |||||
| { | |||||
| if (_c_obj && sord_node_get_type(_c_obj) == SORD_LITERAL) { | |||||
| const SordNode* datatype = sord_node_get_datatype(_c_obj); | |||||
| if (datatype && !strcmp((const char*)sord_node_get_string(datatype), | |||||
| type_uri)) | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| inline int | |||||
| Node::to_int() const | |||||
| { | |||||
| assert(is_int()); | |||||
| char* endptr; | |||||
| return strtol((const char*)sord_node_get_string(_c_obj), &endptr, 10); | |||||
| } | |||||
| inline float | |||||
| Node::to_float() const | |||||
| { | |||||
| assert(is_float()); | |||||
| char* endptr; | |||||
| return serd_strtod((const char*)sord_node_get_string(_c_obj), &endptr); | |||||
| } | |||||
| inline bool | |||||
| Node::to_bool() const | |||||
| { | |||||
| assert(is_bool()); | |||||
| return !strcmp((const char*)sord_node_get_string(_c_obj), "true"); | |||||
| } | |||||
| struct Iter : public Wrapper<SordIter*> { | |||||
| inline Iter(World& world, SordIter* c_obj) | |||||
| : Wrapper<SordIter*>(c_obj), _world(world) {} | |||||
| inline ~Iter() { sord_iter_free(_c_obj); } | |||||
| inline bool end() const { return sord_iter_end(_c_obj); } | |||||
| inline bool next() const { return sord_iter_next(_c_obj); } | |||||
| inline Iter& operator++() { | |||||
| assert(!end()); | |||||
| next(); | |||||
| return *this; | |||||
| } | |||||
| inline const Node get_subject() const { | |||||
| SordQuad quad; | |||||
| sord_iter_get(_c_obj, quad); | |||||
| return Node(_world, quad[SORD_SUBJECT]); | |||||
| } | |||||
| inline const Node get_predicate() const { | |||||
| SordQuad quad; | |||||
| sord_iter_get(_c_obj, quad); | |||||
| return Node(_world, quad[SORD_PREDICATE]); | |||||
| } | |||||
| inline const Node get_object() const { | |||||
| SordQuad quad; | |||||
| sord_iter_get(_c_obj, quad); | |||||
| return Node(_world, quad[SORD_OBJECT]); | |||||
| } | |||||
| World& _world; | |||||
| }; | |||||
| /** An RDF Model (collection of triples). | |||||
| */ | |||||
| class Model : public Noncopyable, public Wrapper<SordModel*> { | |||||
| public: | |||||
| inline Model(World& world, | |||||
| const std::string& base_uri, | |||||
| unsigned indices = (SORD_SPO | SORD_OPS), | |||||
| bool graphs = true); | |||||
| inline ~Model(); | |||||
| inline const Node& base_uri() const { return _base; } | |||||
| size_t num_quads() const { return sord_num_quads(_c_obj); } | |||||
| inline void load_file(SerdEnv* env, | |||||
| SerdSyntax syntax, | |||||
| const std::string& uri, | |||||
| const std::string& base_uri=""); | |||||
| inline void load_string(SerdEnv* env, | |||||
| SerdSyntax syntax, | |||||
| const char* str, | |||||
| size_t len, | |||||
| const std::string& base_uri); | |||||
| inline SerdStatus write_to_file( | |||||
| const std::string& uri, | |||||
| SerdSyntax syntax = SERD_TURTLE, | |||||
| SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED | |||||
| |SERD_STYLE_CURIED | |||||
| |SERD_STYLE_RESOLVED)); | |||||
| inline std::string write_to_string( | |||||
| const std::string& base_uri, | |||||
| SerdSyntax syntax = SERD_TURTLE, | |||||
| SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED | |||||
| |SERD_STYLE_CURIED | |||||
| |SERD_STYLE_RESOLVED)); | |||||
| inline void add_statement(const Node& subject, | |||||
| const Node& predicate, | |||||
| const Node& object); | |||||
| inline Iter find(const Node& subject, | |||||
| const Node& predicate, | |||||
| const Node& object); | |||||
| inline Node get(const Node& subject, | |||||
| const Node& predicate, | |||||
| const Node& object); | |||||
| inline World& world() const { return _world; } | |||||
| private: | |||||
| World& _world; | |||||
| Node _base; | |||||
| }; | |||||
| /** Create an empty in-memory RDF model. | |||||
| */ | |||||
| inline | |||||
| Model::Model(World& world, | |||||
| const std::string& base_uri, | |||||
| unsigned indices, | |||||
| bool graphs) | |||||
| : _world(world) | |||||
| , _base(world, Node::URI, base_uri) | |||||
| { | |||||
| _c_obj = sord_new(_world.world(), indices, graphs); | |||||
| } | |||||
| inline void | |||||
| Model::load_string(SerdEnv* env, | |||||
| SerdSyntax syntax, | |||||
| const char* str, | |||||
| size_t len, | |||||
| const std::string& base_uri) | |||||
| { | |||||
| SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL); | |||||
| serd_reader_read_string(reader, (const uint8_t*)str); | |||||
| serd_reader_free(reader); | |||||
| } | |||||
| inline Model::~Model() | |||||
| { | |||||
| sord_free(_c_obj); | |||||
| } | |||||
| inline void | |||||
| Model::load_file(SerdEnv* env, | |||||
| SerdSyntax syntax, | |||||
| const std::string& data_uri, | |||||
| const std::string& base_uri) | |||||
| { | |||||
| uint8_t* path = serd_file_uri_parse((const uint8_t*)data_uri.c_str(), NULL); | |||||
| if (!path) { | |||||
| fprintf(stderr, "Failed to parse file URI <%s>\n", data_uri.c_str()); | |||||
| return; | |||||
| } | |||||
| // FIXME: blank prefix parameter? | |||||
| SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL); | |||||
| serd_reader_read_file(reader, path); | |||||
| serd_reader_free(reader); | |||||
| free(path); | |||||
| } | |||||
| inline SerdStatus | |||||
| Model::write_to_file(const std::string& uri, SerdSyntax syntax, SerdStyle style) | |||||
| { | |||||
| uint8_t* path = serd_file_uri_parse((const uint8_t*)uri.c_str(), NULL); | |||||
| if (!path) { | |||||
| fprintf(stderr, "Failed to parse file URI <%s>\n", uri.c_str()); | |||||
| return SERD_ERR_BAD_ARG; | |||||
| } | |||||
| FILE* const fd = fopen((const char*)path, "w"); | |||||
| if (!fd) { | |||||
| fprintf(stderr, "Failed to open file %s\n", path); | |||||
| free(path); | |||||
| return SERD_ERR_UNKNOWN; | |||||
| } | |||||
| free(path); | |||||
| SerdURI base_uri = SERD_URI_NULL; | |||||
| if (serd_uri_parse((const uint8_t*)uri.c_str(), &base_uri)) { | |||||
| fprintf(stderr, "Invalid base URI <%s>\n", uri.c_str()); | |||||
| fclose(fd); | |||||
| return SERD_ERR_BAD_ARG; | |||||
| } | |||||
| SerdWriter* writer = serd_writer_new(syntax, | |||||
| style, | |||||
| _world.prefixes().c_obj(), | |||||
| &base_uri, | |||||
| serd_file_sink, | |||||
| fd); | |||||
| serd_env_foreach(_world.prefixes().c_obj(), | |||||
| (SerdPrefixSink)serd_writer_set_prefix, | |||||
| writer); | |||||
| sord_write(_c_obj, writer, 0); | |||||
| serd_writer_free(writer); | |||||
| fclose(fd); | |||||
| return SERD_SUCCESS; | |||||
| } | |||||
| static size_t | |||||
| string_sink(const void* buf, size_t len, void* stream) | |||||
| { | |||||
| std::string* str = (std::string*)stream; | |||||
| str->append((const char*)buf, len); | |||||
| return len; | |||||
| } | |||||
| inline std::string | |||||
| Model::write_to_string(const std::string& base_uri_str, | |||||
| SerdSyntax syntax, | |||||
| SerdStyle style) | |||||
| { | |||||
| SerdURI base_uri = SERD_URI_NULL; | |||||
| if (serd_uri_parse((const uint8_t*)base_uri_str.c_str(), &base_uri)) { | |||||
| fprintf(stderr, "Invalid base URI <%s>\n", base_uri_str.c_str()); | |||||
| return ""; | |||||
| } | |||||
| std::string ret; | |||||
| SerdWriter* writer = serd_writer_new(syntax, | |||||
| style, | |||||
| _world.prefixes().c_obj(), | |||||
| &base_uri, | |||||
| string_sink, | |||||
| &ret); | |||||
| const SerdNode base_uri_node = serd_node_from_string( | |||||
| SERD_URI, (const uint8_t*)base_uri_str.c_str()); | |||||
| serd_writer_set_base_uri(writer, &base_uri_node); | |||||
| serd_env_foreach(_world.prefixes().c_obj(), | |||||
| (SerdPrefixSink)serd_writer_set_prefix, | |||||
| writer); | |||||
| sord_write(_c_obj, writer, 0); | |||||
| serd_writer_free(writer); | |||||
| return ret; | |||||
| } | |||||
| inline void | |||||
| Model::add_statement(const Node& subject, | |||||
| const Node& predicate, | |||||
| const Node& object) | |||||
| { | |||||
| SordQuad quad = { subject.c_obj(), | |||||
| predicate.c_obj(), | |||||
| object.c_obj(), | |||||
| NULL }; | |||||
| sord_add(_c_obj, quad); | |||||
| } | |||||
| inline Iter | |||||
| Model::find(const Node& subject, | |||||
| const Node& predicate, | |||||
| const Node& object) | |||||
| { | |||||
| SordQuad quad = { subject.c_obj(), | |||||
| predicate.c_obj(), | |||||
| object.c_obj(), | |||||
| NULL }; | |||||
| return Iter(_world, sord_find(_c_obj, quad)); | |||||
| } | |||||
| inline Node | |||||
| Model::get(const Node& subject, | |||||
| const Node& predicate, | |||||
| const Node& object) | |||||
| { | |||||
| SordNode* c_node = sord_get( | |||||
| _c_obj, subject.c_obj(), predicate.c_obj(), object.c_obj(), NULL); | |||||
| Node node(_world, c_node); | |||||
| sord_node_free(_world.c_obj(), c_node); | |||||
| return node; | |||||
| } | |||||
| } // namespace Sord | |||||
| #endif // SORD_SORDMM_HPP | |||||
| @@ -0,0 +1,52 @@ | |||||
| /* | |||||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef SORD_SORD_INTERNAL_H | |||||
| #define SORD_SORD_INTERNAL_H | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| #include "sord/sord.h" | |||||
| #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) | |||||
| # define SORD_UNREACHABLE() __builtin_unreachable() | |||||
| #else | |||||
| # define SORD_UNREACHABLE() assert(false) | |||||
| #endif | |||||
| /** Resource node metadata */ | |||||
| typedef struct { | |||||
| size_t refs_as_obj; ///< References as a quad object | |||||
| } SordResourceMetadata; | |||||
| /** Literal node metadata */ | |||||
| typedef struct { | |||||
| SordNode* datatype; ///< Optional literal data type URI | |||||
| char lang[16]; ///< Optional language tag | |||||
| } SordLiteralMetadata; | |||||
| /** Node */ | |||||
| struct SordNodeImpl { | |||||
| SerdNode node; ///< Serd node | |||||
| size_t refs; ///< Reference count (# of containing quads) | |||||
| union { | |||||
| SordResourceMetadata res; | |||||
| SordLiteralMetadata lit; | |||||
| } meta; | |||||
| }; | |||||
| #endif /* SORD_SORD_INTERNAL_H */ | |||||
| @@ -0,0 +1,722 @@ | |||||
| /* | |||||
| Copyright 2011-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include <stdarg.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "sord/sord.h" | |||||
| static const int DIGITS = 3; | |||||
| static const unsigned n_objects_per = 2; | |||||
| static int n_expected_errors = 0; | |||||
| typedef struct { | |||||
| SordQuad query; | |||||
| int expected_num_results; | |||||
| } QueryTest; | |||||
| #define USTR(s) ((const uint8_t*)(s)) | |||||
| static SordNode* | |||||
| uri(SordWorld* world, int num) | |||||
| { | |||||
| if (num == 0) | |||||
| return 0; | |||||
| char str[] = "eg:000"; | |||||
| char* uri_num = str + 3; // First `0' | |||||
| snprintf(uri_num, DIGITS + 1, "%0*d", DIGITS, num); | |||||
| return sord_new_uri(world, (const uint8_t*)str); | |||||
| } | |||||
| static int | |||||
| test_fail(const char* fmt, ...) | |||||
| { | |||||
| va_list args; | |||||
| va_start(args, fmt); | |||||
| fprintf(stderr, "error: "); | |||||
| vfprintf(stderr, fmt, args); | |||||
| va_end(args); | |||||
| return 1; | |||||
| } | |||||
| static int | |||||
| generate(SordWorld* world, | |||||
| SordModel* sord, | |||||
| size_t n_quads, | |||||
| SordNode* graph) | |||||
| { | |||||
| fprintf(stderr, "Generating %zu (S P *) quads with %u objects each\n", | |||||
| n_quads, n_objects_per); | |||||
| for (size_t i = 0; i < n_quads; ++i) { | |||||
| int num = (i * n_objects_per) + 1; | |||||
| SordNode* ids[2 + n_objects_per]; | |||||
| for (unsigned j = 0; j < 2 + n_objects_per; ++j) { | |||||
| ids[j] = uri(world, num++); | |||||
| } | |||||
| for (unsigned j = 0; j < n_objects_per; ++j) { | |||||
| SordQuad tup = { ids[0], ids[1], ids[2 + j], graph }; | |||||
| if (!sord_add(sord, tup)) { | |||||
| return test_fail("Fail: Failed to add quad\n"); | |||||
| } | |||||
| } | |||||
| for (unsigned j = 0; j < 2 + n_objects_per; ++j) { | |||||
| sord_node_free(world, ids[j]); | |||||
| } | |||||
| } | |||||
| // Add some literals | |||||
| // (98 4 "hello") and (98 4 "hello"^^<5>) | |||||
| SordQuad tup = { 0, 0, 0, 0 }; | |||||
| tup[0] = uri(world, 98); | |||||
| tup[1] = uri(world, 4); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); | |||||
| tup[3] = graph; | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); | |||||
| if (!sord_add(sord, tup)) { | |||||
| return test_fail("Failed to add typed literal\n"); | |||||
| } | |||||
| // (96 4 "hello"^^<4>) and (96 4 "hello"^^<5>) | |||||
| tup[0] = uri(world, 96); | |||||
| tup[1] = uri(world, 4); | |||||
| tup[2] = sord_new_literal(world, uri(world, 4), USTR("hello"), NULL); | |||||
| tup[3] = graph; | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); | |||||
| if (!sord_add(sord, tup)) { | |||||
| return test_fail("Failed to add typed literal\n"); | |||||
| } | |||||
| // (94 5 "hello") and (94 5 "hello"@en-gb) | |||||
| tup[0] = uri(world, 94); | |||||
| tup[1] = uri(world, 5); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); | |||||
| tup[3] = graph; | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); | |||||
| if (!sord_add(sord, tup)) { | |||||
| return test_fail("Failed to add literal with language\n"); | |||||
| } | |||||
| // (92 6 "hello"@en-us) and (92 5 "hello"@en-gb) | |||||
| tup[0] = uri(world, 92); | |||||
| tup[1] = uri(world, 6); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), "en-us"); | |||||
| tup[3] = graph; | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); | |||||
| if (!sord_add(sord, tup)) { | |||||
| return test_fail("Failed to add literal with language\n"); | |||||
| } | |||||
| sord_node_free(world, (SordNode*)tup[0]); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[0] = uri(world, 14); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("bonjour"), "fr"); | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("salut"), "fr"); | |||||
| sord_add(sord, tup); | |||||
| // Attempt to add some duplicates | |||||
| if (sord_add(sord, tup)) { | |||||
| return test_fail("Fail: Successfully added duplicate quad\n"); | |||||
| } | |||||
| if (sord_add(sord, tup)) { | |||||
| return test_fail("Fail: Successfully added duplicate quad\n"); | |||||
| } | |||||
| // Add a blank node subject | |||||
| sord_node_free(world, (SordNode*)tup[0]); | |||||
| tup[0] = sord_new_blank(world, USTR("ablank")); | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[1]); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[1] = uri(world, 6); | |||||
| tup[2] = uri(world, 7); | |||||
| sord_add(sord, tup); | |||||
| sord_node_free(world, (SordNode*)tup[0]); | |||||
| sord_node_free(world, (SordNode*)tup[1]); | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| return EXIT_SUCCESS; | |||||
| } | |||||
| #define TUP_FMT "(%6s %6s %6s)" | |||||
| #define TUP_FMT_ARGS(t) \ | |||||
| ((t)[0] ? sord_node_get_string((t)[0]) : USTR("*")), \ | |||||
| ((t)[1] ? sord_node_get_string((t)[1]) : USTR("*")), \ | |||||
| ((t)[2] ? sord_node_get_string((t)[2]) : USTR("*")) | |||||
| static int | |||||
| test_read(SordWorld* world, SordModel* sord, SordNode* g, | |||||
| const size_t n_quads) | |||||
| { | |||||
| int ret = EXIT_SUCCESS; | |||||
| SordQuad id; | |||||
| SordIter* iter = sord_begin(sord); | |||||
| if (sord_iter_get_model(iter) != sord) { | |||||
| return test_fail("Fail: Iterator has incorrect sord pointer\n"); | |||||
| } | |||||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) | |||||
| sord_iter_get(iter, id); | |||||
| // Attempt to increment past end | |||||
| if (!sord_iter_next(iter)) { | |||||
| return test_fail("Fail: Successfully incremented past end\n"); | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| const uint8_t* s = USTR("hello"); | |||||
| SordNode* plain_hello = sord_new_literal(world, 0, s, NULL); | |||||
| SordNode* type4_hello = sord_new_literal(world, uri(world, 4), s, NULL); | |||||
| SordNode* type5_hello = sord_new_literal(world, uri(world, 5), s, NULL); | |||||
| SordNode* gb_hello = sord_new_literal(world, NULL, s, "en-gb"); | |||||
| SordNode* us_hello = sord_new_literal(world, NULL, s, "en-us"); | |||||
| #define NUM_PATTERNS 18 | |||||
| QueryTest patterns[NUM_PATTERNS] = { | |||||
| { { 0, 0, 0 }, (n_quads * n_objects_per) + 12 }, | |||||
| { { uri(world, 1), 0, 0 }, 2 }, | |||||
| { { uri(world, 9), uri(world, 9), uri(world, 9) }, 0 }, | |||||
| { { uri(world, 1), uri(world, 2), uri(world, 4) }, 1 }, | |||||
| { { uri(world, 3), uri(world, 4), uri(world, 0) }, 2 }, | |||||
| { { uri(world, 0), uri(world, 2), uri(world, 4) }, 1 }, | |||||
| { { uri(world, 0), uri(world, 0), uri(world, 4) }, 1 }, | |||||
| { { uri(world, 1), uri(world, 0), uri(world, 0) }, 2 }, | |||||
| { { uri(world, 1), uri(world, 0), uri(world, 4) }, 1 }, | |||||
| { { uri(world, 0), uri(world, 2), uri(world, 0) }, 2 }, | |||||
| { { uri(world, 98), uri(world, 4), plain_hello }, 1 }, | |||||
| { { uri(world, 98), uri(world, 4), type5_hello }, 1 }, | |||||
| { { uri(world, 96), uri(world, 4), type4_hello }, 1 }, | |||||
| { { uri(world, 96), uri(world, 4), type5_hello }, 1 }, | |||||
| { { uri(world, 94), uri(world, 5), plain_hello }, 1 }, | |||||
| { { uri(world, 94), uri(world, 5), gb_hello }, 1 }, | |||||
| { { uri(world, 92), uri(world, 6), gb_hello }, 1 }, | |||||
| { { uri(world, 92), uri(world, 6), us_hello }, 1 } }; | |||||
| SordQuad match = { uri(world, 1), uri(world, 2), uri(world, 4), g }; | |||||
| if (!sord_contains(sord, match)) { | |||||
| return test_fail("Fail: No match for " TUP_FMT "\n", | |||||
| TUP_FMT_ARGS(match)); | |||||
| } | |||||
| SordQuad nomatch = { uri(world, 1), uri(world, 2), uri(world, 9), g }; | |||||
| if (sord_contains(sord, nomatch)) { | |||||
| return test_fail("Fail: False match for " TUP_FMT "\n", | |||||
| TUP_FMT_ARGS(nomatch)); | |||||
| } | |||||
| if (sord_get(sord, NULL, NULL, uri(world, 3), g)) { | |||||
| return test_fail("Fail: Get *,*,3 succeeded\n"); | |||||
| } else if (!sord_node_equals( | |||||
| sord_get(sord, uri(world, 1), uri(world, 2), NULL, g), | |||||
| uri(world, 3))) { | |||||
| return test_fail("Fail: Get 1,2,* != 3\n"); | |||||
| } else if (!sord_node_equals( | |||||
| sord_get(sord, uri(world, 1), NULL, uri(world, 3), g), | |||||
| uri(world, 2))) { | |||||
| return test_fail("Fail: Get 1,*,3 != 2\n"); | |||||
| } else if (!sord_node_equals( | |||||
| sord_get(sord, NULL, uri(world, 2), uri(world, 3), g), | |||||
| uri(world, 1))) { | |||||
| return test_fail("Fail: Get *,2,3 != 1\n"); | |||||
| } | |||||
| for (unsigned i = 0; i < NUM_PATTERNS; ++i) { | |||||
| QueryTest test = patterns[i]; | |||||
| SordQuad pat = { test.query[0], test.query[1], test.query[2], g }; | |||||
| fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat)); | |||||
| iter = sord_find(sord, pat); | |||||
| int num_results = 0; | |||||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| sord_iter_get(iter, id); | |||||
| ++num_results; | |||||
| if (!sord_quad_match(pat, id)) { | |||||
| sord_iter_free(iter); | |||||
| return test_fail( | |||||
| "Fail: Query result " TUP_FMT " does not match pattern\n", | |||||
| TUP_FMT_ARGS(id)); | |||||
| } | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| if (num_results != test.expected_num_results) { | |||||
| return test_fail("Fail: Expected %d results, got %d\n", | |||||
| test.expected_num_results, num_results); | |||||
| } | |||||
| fprintf(stderr, "OK (%u matches)\n", test.expected_num_results); | |||||
| } | |||||
| // Query blank node subject | |||||
| SordQuad pat = { sord_new_blank(world, USTR("ablank")), 0, 0 }; | |||||
| if (!pat[0]) { | |||||
| return test_fail("Blank node subject lost\n"); | |||||
| } | |||||
| fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat)); | |||||
| iter = sord_find(sord, pat); | |||||
| int num_results = 0; | |||||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| sord_iter_get(iter, id); | |||||
| ++num_results; | |||||
| if (!sord_quad_match(pat, id)) { | |||||
| sord_iter_free(iter); | |||||
| return test_fail( | |||||
| "Fail: Query result " TUP_FMT " does not match pattern\n", | |||||
| TUP_FMT_ARGS(id)); | |||||
| } | |||||
| } | |||||
| fprintf(stderr, "OK\n"); | |||||
| sord_node_free(world, (SordNode*)pat[0]); | |||||
| sord_iter_free(iter); | |||||
| if (num_results != 2) { | |||||
| return test_fail("Blank node subject query failed\n"); | |||||
| } | |||||
| // Test nested queries | |||||
| fprintf(stderr, "Nested Queries... "); | |||||
| const SordNode* last_subject = 0; | |||||
| iter = sord_search(sord, NULL, NULL, NULL, NULL); | |||||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| sord_iter_get(iter, id); | |||||
| if (id[0] == last_subject) | |||||
| continue; | |||||
| SordQuad subpat = { id[0], 0, 0 }; | |||||
| SordIter* subiter = sord_find(sord, subpat); | |||||
| uint64_t num_sub_results = 0; | |||||
| if (sord_iter_get_node(subiter, SORD_SUBJECT) != id[0]) { | |||||
| return test_fail("Fail: Incorrect initial submatch\n"); | |||||
| } | |||||
| for (; !sord_iter_end(subiter); sord_iter_next(subiter)) { | |||||
| SordQuad subid; | |||||
| sord_iter_get(subiter, subid); | |||||
| if (!sord_quad_match(subpat, subid)) { | |||||
| sord_iter_free(iter); | |||||
| sord_iter_free(subiter); | |||||
| return test_fail( | |||||
| "Fail: Nested query result does not match pattern\n"); | |||||
| } | |||||
| ++num_sub_results; | |||||
| } | |||||
| sord_iter_free(subiter); | |||||
| if (num_sub_results != n_objects_per) { | |||||
| return test_fail( | |||||
| "Fail: Nested query " TUP_FMT " failed" | |||||
| " (%d results, expected %d)\n", | |||||
| TUP_FMT_ARGS(subpat), num_sub_results, n_objects_per); | |||||
| } | |||||
| uint64_t count = sord_count(sord, id[0], 0, 0, 0); | |||||
| if (count != num_sub_results) { | |||||
| return test_fail("Fail: Query " TUP_FMT " sord_count() %d" | |||||
| "does not match result count %d\n", | |||||
| TUP_FMT_ARGS(subpat), count, num_sub_results); | |||||
| } | |||||
| last_subject = id[0]; | |||||
| } | |||||
| fprintf(stderr, "OK\n\n"); | |||||
| sord_iter_free(iter); | |||||
| return ret; | |||||
| } | |||||
| static SerdStatus | |||||
| unexpected_error(void* handle, const SerdError* error) | |||||
| { | |||||
| fprintf(stderr, "unexpected error: "); | |||||
| vfprintf(stderr, error->fmt, *error->args); | |||||
| return SERD_SUCCESS; | |||||
| } | |||||
| static SerdStatus | |||||
| expected_error(void* handle, const SerdError* error) | |||||
| { | |||||
| fprintf(stderr, "expected error: "); | |||||
| vfprintf(stderr, error->fmt, *error->args); | |||||
| ++n_expected_errors; | |||||
| return SERD_SUCCESS; | |||||
| } | |||||
| int | |||||
| main(int argc, char** argv) | |||||
| { | |||||
| static const size_t n_quads = 300; | |||||
| sord_free(NULL); // Shouldn't crash | |||||
| SordWorld* world = sord_world_new(); | |||||
| // Attempt to create invalid URI | |||||
| fprintf(stderr, "expected "); | |||||
| SordNode* bad_uri = sord_new_uri(world, USTR("noscheme")); | |||||
| if (bad_uri) { | |||||
| return test_fail("Successfully created invalid URI \"noscheme\"\n"); | |||||
| } | |||||
| sord_node_free(world, bad_uri); | |||||
| sord_world_set_error_sink(world, expected_error, NULL); | |||||
| // Attempt to create invalid CURIE | |||||
| SerdNode base = serd_node_from_string(SERD_URI, USTR("http://example.org/")); | |||||
| SerdEnv* env = serd_env_new(&base); | |||||
| SerdNode sbadns = serd_node_from_string(SERD_CURIE, USTR("badns:")); | |||||
| SordNode* badns = sord_node_from_serd_node(world, env, &sbadns, NULL, NULL); | |||||
| if (badns) { | |||||
| return test_fail("Successfully created CURIE with bad namespace\n"); | |||||
| } | |||||
| sord_node_free(world, badns); | |||||
| serd_env_free(env); | |||||
| // Attempt to create NULL node | |||||
| SordNode* nil_node = sord_node_from_serd_node( | |||||
| world, NULL, &SERD_NODE_NULL, NULL, NULL); | |||||
| if (nil_node) { | |||||
| return test_fail("Successfully created NULL node\n"); | |||||
| } | |||||
| sord_node_free(world, nil_node); | |||||
| // Attempt to double-free a node | |||||
| SordNode* garbage = sord_new_uri(world, USTR("urn:garbage")); | |||||
| sord_node_free(world, garbage); | |||||
| sord_world_set_error_sink(world, expected_error, NULL); | |||||
| sord_node_free(world, garbage); | |||||
| sord_world_set_error_sink(world, unexpected_error, NULL); | |||||
| if (n_expected_errors != 2) { | |||||
| return test_fail("Successfully freed node twice\n"); | |||||
| } | |||||
| sord_world_set_error_sink(world, unexpected_error, NULL); | |||||
| // Check node flags are set properly | |||||
| SordNode* with_newline = sord_new_literal(world, NULL, USTR("a\nb"), NULL); | |||||
| if (!(sord_node_get_flags(with_newline) & SERD_HAS_NEWLINE)) { | |||||
| return test_fail("Newline flag not set\n"); | |||||
| } | |||||
| SordNode* with_quote = sord_new_literal(world, NULL, USTR("a\"b"), NULL); | |||||
| if (!(sord_node_get_flags(with_quote) & SERD_HAS_QUOTE)) { | |||||
| return test_fail("Quote flag not set\n"); | |||||
| } | |||||
| // Create with minimal indexing | |||||
| SordModel* sord = sord_new(world, SORD_SPO, false); | |||||
| generate(world, sord, n_quads, NULL); | |||||
| if (test_read(world, sord, NULL, n_quads)) { | |||||
| sord_free(sord); | |||||
| sord_world_free(world); | |||||
| return EXIT_FAILURE; | |||||
| } | |||||
| // Check adding tuples with NULL fields fails | |||||
| sord_world_set_error_sink(world, expected_error, NULL); | |||||
| const size_t initial_num_quads = sord_num_quads(sord); | |||||
| SordQuad tup = { 0, 0, 0, 0}; | |||||
| if (sord_add(sord, tup)) { | |||||
| return test_fail("Added NULL tuple\n"); | |||||
| } | |||||
| tup[0] = uri(world, 1); | |||||
| if (sord_add(sord, tup)) { | |||||
| return test_fail("Added tuple with NULL P and O\n"); | |||||
| } | |||||
| tup[1] = uri(world, 2); | |||||
| if (sord_add(sord, tup)) { | |||||
| return test_fail("Added tuple with NULL O\n"); | |||||
| } | |||||
| if (sord_num_quads(sord) != initial_num_quads) { | |||||
| return test_fail("Num quads %zu != %zu\n", | |||||
| sord_num_quads(sord), initial_num_quads); | |||||
| } | |||||
| // Check adding tuples with an active iterator fails | |||||
| SordIter* iter = sord_begin(sord); | |||||
| tup[2] = uri(world, 3); | |||||
| if (sord_add(sord, tup)) { | |||||
| return test_fail("Added tuple with active iterator\n"); | |||||
| } | |||||
| // Check removing tuples with several active iterator fails | |||||
| SordIter* iter2 = sord_begin(sord); | |||||
| if (!sord_erase(sord, iter)) { | |||||
| return test_fail("Erased tuple with several active iterators\n"); | |||||
| } | |||||
| n_expected_errors = 0; | |||||
| sord_remove(sord, tup); | |||||
| if (n_expected_errors != 1) { | |||||
| return test_fail("Removed tuple with several active iterators\n"); | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| sord_iter_free(iter2); | |||||
| sord_world_set_error_sink(world, unexpected_error, NULL); | |||||
| // Check interning merges equivalent values | |||||
| SordNode* uri_id = sord_new_uri(world, USTR("http://example.org")); | |||||
| SordNode* blank_id = sord_new_blank(world, USTR("testblank")); | |||||
| SordNode* lit_id = sord_new_literal(world, uri_id, USTR("hello"), NULL); | |||||
| if (sord_node_get_type(uri_id) != SORD_URI) { | |||||
| return test_fail("URI node has incorrect type\n"); | |||||
| } else if (sord_node_get_type(blank_id) != SORD_BLANK) { | |||||
| return test_fail("Blank node has incorrect type\n"); | |||||
| } else if (sord_node_get_type(lit_id) != SORD_LITERAL) { | |||||
| return test_fail("Literal node has incorrect type\n"); | |||||
| } | |||||
| const size_t initial_num_nodes = sord_num_nodes(world); | |||||
| SordNode* uri_id2 = sord_new_uri(world, USTR("http://example.org")); | |||||
| SordNode* blank_id2 = sord_new_blank(world, USTR("testblank")); | |||||
| SordNode* lit_id2 = sord_new_literal(world, uri_id, USTR("hello"), NULL); | |||||
| if (uri_id2 != uri_id || !sord_node_equals(uri_id2, uri_id)) { | |||||
| fprintf(stderr, "Fail: URI interning failed (duplicates)\n"); | |||||
| goto fail; | |||||
| } else if (blank_id2 != blank_id | |||||
| || !sord_node_equals(blank_id2, blank_id)) { | |||||
| fprintf(stderr, "Fail: Blank node interning failed (duplicates)\n"); | |||||
| goto fail; | |||||
| } else if (lit_id2 != lit_id || !sord_node_equals(lit_id2, lit_id)) { | |||||
| fprintf(stderr, "Fail: Literal interning failed (duplicates)\n"); | |||||
| goto fail; | |||||
| } | |||||
| size_t len; | |||||
| const uint8_t* str = sord_node_get_string_counted(lit_id2, &len); | |||||
| if (strcmp((const char*)str, "hello")) { | |||||
| return test_fail("Literal node corrupt\n"); | |||||
| } else if (len != strlen("hello")) { | |||||
| return test_fail("Literal length incorrect\n"); | |||||
| } | |||||
| if (sord_num_nodes(world) != initial_num_nodes) { | |||||
| return test_fail("Num nodes %zu != %zu\n", | |||||
| sord_num_nodes(world), initial_num_nodes); | |||||
| } | |||||
| // Check interning doesn't clash non-equivalent values | |||||
| SordNode* uri_id3 = sord_new_uri(world, USTR("http://example.orgX")); | |||||
| SordNode* blank_id3 = sord_new_blank(world, USTR("testblankX")); | |||||
| SordNode* lit_id3 = sord_new_literal(world, uri_id, USTR("helloX"), NULL); | |||||
| if (uri_id3 == uri_id || sord_node_equals(uri_id3, uri_id)) { | |||||
| fprintf(stderr, "Fail: URI interning failed (clash)\n"); | |||||
| goto fail; | |||||
| } else if (blank_id3 == blank_id || sord_node_equals(blank_id3, blank_id)) { | |||||
| fprintf(stderr, "Fail: Blank node interning failed (clash)\n"); | |||||
| goto fail; | |||||
| } else if (lit_id3 == lit_id || sord_node_equals(lit_id3, lit_id)) { | |||||
| fprintf(stderr, "Fail: Literal interning failed (clash)\n"); | |||||
| goto fail; | |||||
| } | |||||
| // Check literal interning | |||||
| SordNode* lit4 = sord_new_literal(world, NULL, USTR("hello"), NULL); | |||||
| SordNode* lit5 = sord_new_literal(world, uri_id2, USTR("hello"), NULL); | |||||
| SordNode* lit6 = sord_new_literal(world, NULL, USTR("hello"), "en-ca"); | |||||
| if (lit4 == lit5 || sord_node_equals(lit4, lit5) | |||||
| || lit4 == lit6 || sord_node_equals(lit4, lit6) | |||||
| || lit5 == lit6 || sord_node_equals(lit5, lit6)) { | |||||
| fprintf(stderr, "Fail: Literal interning failed (type/lang clash)\n"); | |||||
| goto fail; | |||||
| } | |||||
| // Check relative URI construction | |||||
| SordNode* reluri = sord_new_relative_uri( | |||||
| world, USTR("a/b"), USTR("http://example.org/")); | |||||
| if (strcmp((const char*)sord_node_get_string(reluri), | |||||
| "http://example.org/a/b")) { | |||||
| fprintf(stderr, "Fail: Bad relative URI constructed: <%s>\n", | |||||
| sord_node_get_string(reluri)); | |||||
| goto fail; | |||||
| } | |||||
| SordNode* reluri2 = sord_new_relative_uri( | |||||
| world, USTR("http://drobilla.net/"), USTR("http://example.org/")); | |||||
| if (strcmp((const char*)sord_node_get_string(reluri2), | |||||
| "http://drobilla.net/")) { | |||||
| fprintf(stderr, "Fail: Bad relative URI constructed: <%s>\n", | |||||
| sord_node_get_string(reluri)); | |||||
| goto fail; | |||||
| } | |||||
| // Check comparison with NULL | |||||
| sord_node_free(world, uri_id); | |||||
| sord_node_free(world, blank_id); | |||||
| sord_node_free(world, lit_id); | |||||
| sord_node_free(world, uri_id2); | |||||
| sord_node_free(world, blank_id2); | |||||
| sord_node_free(world, lit_id2); | |||||
| sord_node_free(world, uri_id3); | |||||
| sord_node_free(world, blank_id3); | |||||
| sord_node_free(world, lit_id3); | |||||
| sord_free(sord); | |||||
| static const char* const index_names[6] = { | |||||
| "spo", "sop", "ops", "osp", "pso", "pos" | |||||
| }; | |||||
| for (int i = 0; i < 6; ++i) { | |||||
| sord = sord_new(world, (1 << i), false); | |||||
| printf("Testing Index `%s'\n", index_names[i]); | |||||
| generate(world, sord, n_quads, 0); | |||||
| if (test_read(world, sord, 0, n_quads)) | |||||
| goto fail; | |||||
| sord_free(sord); | |||||
| } | |||||
| static const char* const graph_index_names[6] = { | |||||
| "gspo", "gsop", "gops", "gosp", "gpso", "gpos" | |||||
| }; | |||||
| for (int i = 0; i < 6; ++i) { | |||||
| sord = sord_new(world, (1 << i), true); | |||||
| printf("Testing Index `%s'\n", graph_index_names[i]); | |||||
| SordNode* graph = uri(world, 42); | |||||
| generate(world, sord, n_quads, graph); | |||||
| if (test_read(world, sord, graph, n_quads)) | |||||
| goto fail; | |||||
| sord_free(sord); | |||||
| } | |||||
| // Test removing | |||||
| sord = sord_new(world, SORD_SPO, true); | |||||
| tup[0] = uri(world, 1); | |||||
| tup[1] = uri(world, 2); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); | |||||
| tup[3] = 0; | |||||
| sord_add(sord, tup); | |||||
| if (!sord_ask(sord, tup[0], tup[1], tup[2], tup[3])) { | |||||
| fprintf(stderr, "Failed to add tuple\n"); | |||||
| goto fail; | |||||
| } | |||||
| sord_node_free(world, (SordNode*)tup[2]); | |||||
| tup[2] = sord_new_literal(world, 0, USTR("hi"), NULL); | |||||
| sord_add(sord, tup); | |||||
| sord_remove(sord, tup); | |||||
| if (sord_num_quads(sord) != 1) { | |||||
| fprintf(stderr, "Remove failed (%zu quads, expected 1)\n", | |||||
| sord_num_quads(sord)); | |||||
| goto fail; | |||||
| } | |||||
| iter = sord_find(sord, tup); | |||||
| if (!sord_iter_end(iter)) { | |||||
| fprintf(stderr, "Found removed tuple\n"); | |||||
| goto fail; | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| // Load a couple graphs | |||||
| SordNode* graph42 = uri(world, 42); | |||||
| SordNode* graph43 = uri(world, 43); | |||||
| generate(world, sord, 1, graph42); | |||||
| generate(world, sord, 1, graph43); | |||||
| // Remove one graph via iterator | |||||
| SerdStatus st; | |||||
| iter = sord_search(sord, NULL, NULL, NULL, graph43); | |||||
| while (!sord_iter_end(iter)) { | |||||
| if ((st = sord_erase(sord, iter))) { | |||||
| fprintf(stderr, "Remove by iterator failed (%s)\n", | |||||
| serd_strerror(st)); | |||||
| goto fail; | |||||
| } | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| // Ensure only the other graph is left | |||||
| SordQuad quad; | |||||
| SordQuad pat = { 0, 0, 0, graph42 }; | |||||
| for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| sord_iter_get(iter, quad); | |||||
| if (!sord_quad_match(quad, pat)) { | |||||
| fprintf(stderr, "Graph removal via iteration failed\n"); | |||||
| goto fail; | |||||
| } | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| // Load file into two separate graphs | |||||
| sord_free(sord); | |||||
| sord = sord_new(world, SORD_SPO, true); | |||||
| env = serd_env_new(&base); | |||||
| SordNode* graph1 = sord_new_uri(world, USTR("http://example.org/graph1")); | |||||
| SordNode* graph2 = sord_new_uri(world, USTR("http://example.org/graph2")); | |||||
| SerdReader* reader = sord_new_reader(sord, env, SERD_TURTLE, graph1); | |||||
| if ((st = serd_reader_read_string(reader, USTR("<s> <p> <o> .")))) { | |||||
| fprintf(stderr, "Failed to read string (%s)\n", serd_strerror(st)); | |||||
| goto fail; | |||||
| } | |||||
| serd_reader_free(reader); | |||||
| reader = sord_new_reader(sord, env, SERD_TURTLE, graph2); | |||||
| if ((st = serd_reader_read_string(reader, USTR("<s> <p> <o> .")))) { | |||||
| fprintf(stderr, "Failed to re-read string (%s)\n", serd_strerror(st)); | |||||
| goto fail; | |||||
| } | |||||
| // Ensure we only see triple once | |||||
| size_t n_triples = 0; | |||||
| for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| fprintf(stderr, "%s %s %s %s\n", | |||||
| sord_node_get_string(sord_iter_get_node(iter, SORD_SUBJECT)), | |||||
| sord_node_get_string(sord_iter_get_node(iter, SORD_PREDICATE)), | |||||
| sord_node_get_string(sord_iter_get_node(iter, SORD_OBJECT)), | |||||
| sord_node_get_string(sord_iter_get_node(iter, SORD_GRAPH))); | |||||
| ++n_triples; | |||||
| } | |||||
| if (n_triples != 1) { | |||||
| fprintf(stderr, "Found duplicate triple\n"); | |||||
| goto fail; | |||||
| } | |||||
| // Test SPO iteration on an SOP indexed store | |||||
| sord_free(sord); | |||||
| sord = sord_new(world, SORD_SOP, false); | |||||
| generate(world, sord, 1, graph42); | |||||
| for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| ++n_triples; | |||||
| } | |||||
| sord_free(sord); | |||||
| sord_world_free(world); | |||||
| return EXIT_SUCCESS; | |||||
| fail: | |||||
| sord_free(sord); | |||||
| sord_world_free(world); | |||||
| return EXIT_FAILURE; | |||||
| } | |||||
| @@ -0,0 +1,746 @@ | |||||
| /* | |||||
| Copyright 2012-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #define _BSD_SOURCE // for realpath | |||||
| #include <assert.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #ifdef _WIN32 | |||||
| # include <windows.h> | |||||
| #endif | |||||
| #include "serd/serd.h" | |||||
| #include "sord/sord.h" | |||||
| #include "sord_config.h" | |||||
| #ifdef HAVE_PCRE | |||||
| # include <pcre.h> | |||||
| #endif | |||||
| #define USTR(s) ((const uint8_t*)s) | |||||
| #define NS_foaf (const uint8_t*)"http://xmlns.com/foaf/0.1/" | |||||
| #define NS_owl (const uint8_t*)"http://www.w3.org/2002/07/owl#" | |||||
| #define NS_rdf (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||||
| #define NS_rdfs (const uint8_t*)"http://www.w3.org/2000/01/rdf-schema#" | |||||
| #define NS_xsd (const uint8_t*)"http://www.w3.org/2001/XMLSchema#" | |||||
| typedef struct { | |||||
| SordNode* foaf_Document; | |||||
| SordNode* owl_AnnotationProperty; | |||||
| SordNode* owl_Class; | |||||
| SordNode* owl_DatatypeProperty; | |||||
| SordNode* owl_FunctionalProperty; | |||||
| SordNode* owl_InverseFunctionalProperty; | |||||
| SordNode* owl_ObjectProperty; | |||||
| SordNode* owl_OntologyProperty; | |||||
| SordNode* owl_Restriction; | |||||
| SordNode* owl_Thing; | |||||
| SordNode* owl_cardinality; | |||||
| SordNode* owl_equivalentClass; | |||||
| SordNode* owl_minCardinality; | |||||
| SordNode* owl_onDatatype; | |||||
| SordNode* owl_onProperty; | |||||
| SordNode* owl_someValuesFrom; | |||||
| SordNode* owl_withRestrictions; | |||||
| SordNode* rdf_PlainLiteral; | |||||
| SordNode* rdf_Property; | |||||
| SordNode* rdf_first; | |||||
| SordNode* rdf_rest; | |||||
| SordNode* rdf_type; | |||||
| SordNode* rdfs_Class; | |||||
| SordNode* rdfs_Literal; | |||||
| SordNode* rdfs_Resource; | |||||
| SordNode* rdfs_domain; | |||||
| SordNode* rdfs_label; | |||||
| SordNode* rdfs_range; | |||||
| SordNode* rdfs_subClassOf; | |||||
| SordNode* xsd_anyURI; | |||||
| SordNode* xsd_decimal; | |||||
| SordNode* xsd_maxInclusive; | |||||
| SordNode* xsd_minInclusive; | |||||
| SordNode* xsd_pattern; | |||||
| SordNode* xsd_string; | |||||
| } URIs; | |||||
| int n_errors = 0; | |||||
| int n_restrictions = 0; | |||||
| bool one_line_errors = false; | |||||
| static int | |||||
| print_version(void) | |||||
| { | |||||
| printf("sord_validate " SORD_VERSION | |||||
| " <http://drobilla.net/software/sord>\n"); | |||||
| printf("Copyright 2012-2013 David Robillard <http://drobilla.net>.\n" | |||||
| "License: <http://www.opensource.org/licenses/isc>\n" | |||||
| "This is free software; you are free to change and redistribute it." | |||||
| "\nThere is NO WARRANTY, to the extent permitted by law.\n"); | |||||
| return 0; | |||||
| } | |||||
| static int | |||||
| print_usage(const char* name, bool error) | |||||
| { | |||||
| FILE* const os = error ? stderr : stdout; | |||||
| fprintf(os, "Usage: %s [OPTION]... INPUT...\n", name); | |||||
| fprintf(os, "Validate RDF data\n\n"); | |||||
| fprintf(os, " -h Display this help and exit\n"); | |||||
| fprintf(os, " -l Print errors on a single line.\n"); | |||||
| fprintf(os, " -v Display version information and exit\n"); | |||||
| fprintf(os, | |||||
| "Validate RDF data. This is a simple validator which checks\n" | |||||
| "that all used properties are actually defined. It does not do\n" | |||||
| "any fancy file retrieval, the files passed on the command line\n" | |||||
| "are the only data that is read. In other words, you must pass\n" | |||||
| "the definition of all vocabularies used on the command line.\n"); | |||||
| return error ? 1 : 0; | |||||
| } | |||||
| static uint8_t* | |||||
| absolute_path(const uint8_t* path) | |||||
| { | |||||
| #ifdef _WIN32 | |||||
| char* out = (char*)malloc(MAX_PATH); | |||||
| GetFullPathName((const char*)path, MAX_PATH, out, NULL); | |||||
| return (uint8_t*)out; | |||||
| #else | |||||
| return (uint8_t*)realpath((const char*)path, NULL); | |||||
| #endif | |||||
| } | |||||
| static int | |||||
| error(const char* msg, const SordQuad quad) | |||||
| { | |||||
| const char* sep = one_line_errors ? "\t" : "\n "; | |||||
| ++n_errors; | |||||
| fprintf(stderr, "error: %s:%s%s%s%s%s%s\n", | |||||
| msg, | |||||
| sep, (const char*)sord_node_get_string(quad[SORD_SUBJECT]), | |||||
| sep, (const char*)sord_node_get_string(quad[SORD_PREDICATE]), | |||||
| sep, (const char*)sord_node_get_string(quad[SORD_OBJECT])); | |||||
| return 1; | |||||
| } | |||||
| static int | |||||
| errorf(const char* fmt, ...) | |||||
| { | |||||
| va_list args; | |||||
| va_start(args, fmt); | |||||
| vfprintf(stderr, fmt, args); | |||||
| va_end(args); | |||||
| return 1; | |||||
| } | |||||
| static bool | |||||
| is_descendant_of(SordModel* model, | |||||
| const URIs* uris, | |||||
| const SordNode* child, | |||||
| const SordNode* parent, | |||||
| const SordNode* pred) | |||||
| { | |||||
| if (!child) { | |||||
| return false; | |||||
| } else if (sord_node_equals(child, parent) || | |||||
| sord_ask(model, child, uris->owl_equivalentClass, parent, NULL)) { | |||||
| return true; | |||||
| } | |||||
| SordIter* i = sord_search(model, child, pred, NULL, NULL); | |||||
| for (; !sord_iter_end(i); sord_iter_next(i)) { | |||||
| const SordNode* o = sord_iter_get_node(i, SORD_OBJECT); | |||||
| if (sord_node_equals(child, o)) { | |||||
| continue; // Weird class is explicitly a descendent of itself | |||||
| } | |||||
| if (is_descendant_of(model, uris, o, parent, pred)) { | |||||
| sord_iter_free(i); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| sord_iter_free(i); | |||||
| return false; | |||||
| } | |||||
| static bool | |||||
| regexp_match(const uint8_t* pat, const char* str) | |||||
| { | |||||
| #ifdef HAVE_PCRE | |||||
| // Append a $ to the pattern so we only match if the entire string matches | |||||
| const size_t len = strlen((const char*)pat); | |||||
| char* const regx = (char*)malloc(len + 2); | |||||
| memcpy(regx, pat, len); | |||||
| regx[len] = '$'; | |||||
| regx[len + 1] = '\0'; | |||||
| const char* err; | |||||
| int erroffset; | |||||
| pcre* re = pcre_compile(regx, PCRE_ANCHORED, &err, &erroffset, NULL); | |||||
| free(regx); | |||||
| if (!re) { | |||||
| fprintf(stderr, "Error in pattern `%s' at offset %d (%s)\n", | |||||
| pat, erroffset, err); | |||||
| return false; | |||||
| } | |||||
| const bool ret = pcre_exec(re, NULL, str, strlen(str), 0, 0, NULL, 0) >= 0; | |||||
| pcre_free(re); | |||||
| return ret; | |||||
| #endif // HAVE_PCRE | |||||
| return true; | |||||
| } | |||||
| static bool | |||||
| check_restriction(SordModel* model, | |||||
| const URIs* uris, | |||||
| const SordNode* literal, | |||||
| const SordNode* type, | |||||
| const SordNode* restriction) | |||||
| { | |||||
| size_t len = 0; | |||||
| const char* str = (const char*)sord_node_get_string_counted(literal, &len); | |||||
| ++n_restrictions; | |||||
| // Check xsd:pattern | |||||
| SordIter* p = sord_search(model, restriction, uris->xsd_pattern, 0, 0); | |||||
| if (p) { | |||||
| const SordNode* pat = sord_iter_get_node(p, SORD_OBJECT); | |||||
| const bool good = regexp_match(sord_node_get_string(pat), str); | |||||
| if (!good) { | |||||
| fprintf(stderr, "`%s' does not match <%s> pattern `%s'\n", | |||||
| sord_node_get_string(literal), | |||||
| sord_node_get_string(type), | |||||
| sord_node_get_string(pat)); | |||||
| } | |||||
| sord_iter_free(p); | |||||
| return good; | |||||
| } | |||||
| /* We'll do some comparison tricks for xsd:decimal types, where | |||||
| lexicographical comparison would be incorrect. Note that if the | |||||
| literal's type is a descendant of xsd:decimal, we'll end up checking it | |||||
| against the xsd:decimal pattern so there's no need to validate digits | |||||
| here. At worst we'll get a false positive but it will fail later. */ | |||||
| const bool is_decimal = is_descendant_of( | |||||
| model, uris, type, uris->xsd_decimal, uris->owl_onDatatype); | |||||
| // Check xsd:minInclusive | |||||
| SordIter* l = sord_search(model, restriction, uris->xsd_minInclusive, 0, 0); | |||||
| if (l) { | |||||
| const SordNode* lower = sord_iter_get_node(l, SORD_OBJECT); | |||||
| size_t lower_len = 0; | |||||
| const char* lower_str = (const char*)sord_node_get_string_counted(lower, &lower_len); | |||||
| bool good = false; | |||||
| if (!is_decimal || len == lower_len) { | |||||
| // Not decimal, or equal lengths, strcmp | |||||
| good = (strcmp(str, lower_str) >= 0); | |||||
| } else { | |||||
| // Decimal with different length, only good if longer than the min | |||||
| good = (len > lower_len); | |||||
| } | |||||
| if (!good) { | |||||
| fprintf(stderr, "`%s' is not >= <%s> minimum `%s'\n", | |||||
| sord_node_get_string(literal), | |||||
| sord_node_get_string(type), | |||||
| sord_node_get_string(lower)); | |||||
| } | |||||
| sord_iter_free(l); | |||||
| return good; | |||||
| } | |||||
| // Check xsd:maxInclusive | |||||
| SordIter* u = sord_search(model, restriction, uris->xsd_maxInclusive, 0, 0); | |||||
| if (u) { | |||||
| const SordNode* upper = sord_iter_get_node(u, SORD_OBJECT); | |||||
| size_t upper_len = 0; | |||||
| const char* upper_str = (const char*)sord_node_get_string_counted(upper, &upper_len); | |||||
| bool good = false; | |||||
| if (!is_decimal || len == upper_len) { | |||||
| // Not decimal, or equal lengths, strcmp | |||||
| good = (strcmp(str, upper_str) <= 0); | |||||
| } else { | |||||
| // Decimal with different length, only good if shorter than the max | |||||
| good = (len < upper_len); | |||||
| } | |||||
| if (!good) { | |||||
| fprintf(stderr, "`%s' is not <= <%s> maximum `%s'\n", | |||||
| sord_node_get_string(literal), | |||||
| sord_node_get_string(type), | |||||
| sord_node_get_string(upper)); | |||||
| } | |||||
| sord_iter_free(u); | |||||
| return good; | |||||
| } | |||||
| --n_restrictions; | |||||
| return true; // Unknown restriction, be quietly tolerant | |||||
| } | |||||
| static bool | |||||
| literal_is_valid(SordModel* model, | |||||
| const URIs* uris, | |||||
| const SordNode* literal, | |||||
| const SordNode* type) | |||||
| { | |||||
| if (!type) { | |||||
| return true; | |||||
| } | |||||
| /* Check that literal data is related to required type. We don't do a | |||||
| strict subtype check here because e.g. an xsd:decimal might be a valid | |||||
| xsd:unsignedInt, which the pattern checks will verify, but if the | |||||
| literal type is not related to the required type at all | |||||
| (e.g. xsd:decimal and xsd:string) there is a problem. */ | |||||
| const SordNode* datatype = sord_node_get_datatype(literal); | |||||
| if (datatype && datatype != type) { | |||||
| if (!is_descendant_of( | |||||
| model, uris, | |||||
| datatype, type, uris->owl_onDatatype) && | |||||
| !is_descendant_of( | |||||
| model, uris, | |||||
| type, datatype, uris->owl_onDatatype)) { | |||||
| errorf("Literal `%s' datatype <%s> is not compatible with <%s>\n", | |||||
| sord_node_get_string(literal), | |||||
| sord_node_get_string(datatype), | |||||
| sord_node_get_string(type)); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| // Find restrictions list | |||||
| SordIter* rs = sord_search(model, type, uris->owl_withRestrictions, 0, 0); | |||||
| if (sord_iter_end(rs)) { | |||||
| return true; // No restrictions | |||||
| } | |||||
| // Walk list, checking each restriction | |||||
| const SordNode* head = sord_iter_get_node(rs, SORD_OBJECT); | |||||
| while (head) { | |||||
| SordIter* f = sord_search(model, head, uris->rdf_first, 0, 0); | |||||
| if (!f) { | |||||
| break; // Reached end of restrictions list without failure | |||||
| } | |||||
| // Check this restriction | |||||
| const bool good = check_restriction( | |||||
| model, uris, literal, type, sord_iter_get_node(f, SORD_OBJECT)); | |||||
| sord_iter_free(f); | |||||
| if (!good) { | |||||
| sord_iter_free(rs); | |||||
| return false; // Failed, literal is invalid | |||||
| } | |||||
| // Seek to next list node | |||||
| SordIter* n = sord_search(model, head, uris->rdf_rest, 0, 0); | |||||
| head = n ? sord_iter_get_node(n, SORD_OBJECT) : NULL; | |||||
| sord_iter_free(n); | |||||
| } | |||||
| sord_iter_free(rs); | |||||
| SordIter* s = sord_search(model, type, uris->owl_onDatatype, 0, 0); | |||||
| if (s) { | |||||
| const SordNode* super = sord_iter_get_node(s, SORD_OBJECT); | |||||
| const bool good = literal_is_valid(model, uris, literal, super); | |||||
| sord_iter_free(s); | |||||
| return good; // Match iff literal also matches supertype | |||||
| } | |||||
| return true; // Matches top level type | |||||
| } | |||||
| static bool | |||||
| check_type(SordModel* model, | |||||
| const URIs* uris, | |||||
| const SordNode* node, | |||||
| const SordNode* type) | |||||
| { | |||||
| if (sord_node_equals(type, uris->rdfs_Resource) || | |||||
| sord_node_equals(type, uris->owl_Thing)) { | |||||
| return true; | |||||
| } | |||||
| if (sord_node_get_type(node) == SORD_LITERAL) { | |||||
| if (sord_node_equals(type, uris->rdfs_Literal)) { | |||||
| return true; | |||||
| } else if (sord_node_equals(type, uris->rdf_PlainLiteral)) { | |||||
| return !sord_node_get_language(node); | |||||
| } else { | |||||
| return literal_is_valid(model, uris, node, type); | |||||
| } | |||||
| } else if (sord_node_get_type(node) == SORD_URI) { | |||||
| if (sord_node_equals(type, uris->foaf_Document)) { | |||||
| return true; // Questionable... | |||||
| } else if (is_descendant_of( | |||||
| model, uris, | |||||
| type, uris->xsd_anyURI, uris->owl_onDatatype)) { | |||||
| /* Type is any URI and this is a URI, so pass. Restrictions on | |||||
| anyURI subtypes are not currently checked (very uncommon). */ | |||||
| return true; // Type is anyURI, and this is a URI | |||||
| } else { | |||||
| SordIter* t = sord_search(model, node, uris->rdf_type, NULL, NULL); | |||||
| for (; !sord_iter_end(t); sord_iter_next(t)) { | |||||
| if (is_descendant_of(model, uris, | |||||
| sord_iter_get_node(t, SORD_OBJECT), | |||||
| type, | |||||
| uris->rdfs_subClassOf)) { | |||||
| sord_iter_free(t); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| sord_iter_free(t); | |||||
| return false; | |||||
| } | |||||
| } else { | |||||
| return true; // Blanks often lack explicit types, ignore | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static int | |||||
| check_properties(SordModel* model, URIs* uris) | |||||
| { | |||||
| int st = 0; | |||||
| SordIter* i = sord_begin(model); | |||||
| for (; !sord_iter_end(i); sord_iter_next(i)) { | |||||
| SordQuad quad; | |||||
| sord_iter_get(i, quad); | |||||
| const SordNode* subj = quad[SORD_SUBJECT]; | |||||
| const SordNode* pred = quad[SORD_PREDICATE]; | |||||
| const SordNode* obj = quad[SORD_OBJECT]; | |||||
| bool is_any_property = false; | |||||
| SordIter* t = sord_search(model, pred, uris->rdf_type, NULL, NULL); | |||||
| for (; !sord_iter_end(t); sord_iter_next(t)) { | |||||
| if (is_descendant_of(model, uris, | |||||
| sord_iter_get_node(t, SORD_OBJECT), | |||||
| uris->rdf_Property, | |||||
| uris->rdfs_subClassOf)) { | |||||
| is_any_property = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| sord_iter_free(t); | |||||
| const bool is_ObjectProperty = sord_ask( | |||||
| model, pred, uris->rdf_type, uris->owl_ObjectProperty, 0); | |||||
| const bool is_FunctionalProperty = sord_ask( | |||||
| model, pred, uris->rdf_type, uris->owl_FunctionalProperty, 0); | |||||
| const bool is_InverseFunctionalProperty = sord_ask( | |||||
| model, pred, uris->rdf_type, uris->owl_InverseFunctionalProperty, 0); | |||||
| const bool is_DatatypeProperty = sord_ask( | |||||
| model, pred, uris->rdf_type, uris->owl_DatatypeProperty, 0); | |||||
| if (!is_any_property) { | |||||
| st = error("Use of undefined property", quad); | |||||
| } | |||||
| if (!sord_ask(model, pred, uris->rdfs_label, NULL, NULL)) { | |||||
| st = errorf("Property <%s> has no label\n", | |||||
| sord_node_get_string(pred)); | |||||
| } | |||||
| if (is_DatatypeProperty && | |||||
| sord_node_get_type(obj) != SORD_LITERAL) { | |||||
| st = error("Datatype property with non-literal value", quad); | |||||
| } | |||||
| if (is_ObjectProperty && | |||||
| sord_node_get_type(obj) == SORD_LITERAL) { | |||||
| st = error("Object property with literal value", quad); | |||||
| } | |||||
| if (is_FunctionalProperty && | |||||
| sord_count(model, subj, pred, NULL, NULL) > 1) { | |||||
| st = error("Functional property with several objects", quad); | |||||
| } | |||||
| if (is_InverseFunctionalProperty && | |||||
| sord_count(model, NULL, pred, obj, NULL) > 1) { | |||||
| st = error("Inverse functional property with several subjects", quad); | |||||
| } | |||||
| if (sord_node_equals(pred, uris->rdf_type) && | |||||
| !sord_ask(model, obj, uris->rdf_type, uris->rdfs_Class, NULL) && | |||||
| !sord_ask(model, obj, uris->rdf_type, uris->owl_Class, NULL)) { | |||||
| st = error("Type is not a rdfs:Class or owl:Class", quad); | |||||
| } | |||||
| if (sord_node_get_type(obj) == SORD_LITERAL && | |||||
| !literal_is_valid(model, uris, obj, sord_node_get_datatype(obj))) { | |||||
| st = error("Literal does not match datatype", quad); | |||||
| } | |||||
| SordIter* r = sord_search(model, pred, uris->rdfs_range, NULL, NULL); | |||||
| for (; !sord_iter_end(r); sord_iter_next(r)) { | |||||
| const SordNode* range = sord_iter_get_node(r, SORD_OBJECT); | |||||
| if (!check_type(model, uris, obj, range)) { | |||||
| st = error("Object not in property range", quad); | |||||
| fprintf(stderr, "note: Range is <%s>\n", | |||||
| sord_node_get_string(range)); | |||||
| } | |||||
| } | |||||
| sord_iter_free(r); | |||||
| SordIter* d = sord_search(model, pred, uris->rdfs_domain, NULL, NULL); | |||||
| if (d) { | |||||
| const SordNode* domain = sord_iter_get_node(d, SORD_OBJECT); | |||||
| if (!check_type(model, uris, subj, domain)) { | |||||
| st = error("Subject not in property domain", quad); | |||||
| fprintf(stderr, "note: Domain is <%s>\n", | |||||
| sord_node_get_string(domain)); | |||||
| } | |||||
| sord_iter_free(d); | |||||
| } | |||||
| } | |||||
| sord_iter_free(i); | |||||
| return st; | |||||
| } | |||||
| static int | |||||
| check_instance(SordModel* model, | |||||
| const URIs* uris, | |||||
| const SordNode* restriction, | |||||
| const SordNode* instance) | |||||
| { | |||||
| int st = 0; | |||||
| const SordNode* prop = sord_get( | |||||
| model, restriction, uris->owl_onProperty, NULL, NULL); | |||||
| if (!prop) { | |||||
| return 0; | |||||
| } | |||||
| const unsigned values = sord_count(model, instance, prop, NULL, NULL); | |||||
| // Check exact cardinality | |||||
| const SordNode* card = sord_get( | |||||
| model, restriction, uris->owl_cardinality, NULL, NULL); | |||||
| if (card) { | |||||
| const unsigned c = atoi((const char*)sord_node_get_string(card)); | |||||
| if (values != c) { | |||||
| st = errorf("Property %s on %s has %u != %u values\n", | |||||
| sord_node_get_string(prop), | |||||
| sord_node_get_string(instance), | |||||
| values, c); | |||||
| } | |||||
| } | |||||
| // Check minimum cardinality | |||||
| const SordNode* minCard = sord_get( | |||||
| model, restriction, uris->owl_minCardinality, NULL, NULL); | |||||
| if (minCard) { | |||||
| const unsigned m = atoi((const char*)sord_node_get_string(minCard)); | |||||
| if (values < m) { | |||||
| st = errorf("Property %s on %s has %u < %u values\n", | |||||
| sord_node_get_string(prop), | |||||
| sord_node_get_string(instance), | |||||
| values, m); | |||||
| } | |||||
| } | |||||
| // Check someValuesFrom | |||||
| SordIter* sf = sord_search( | |||||
| model, restriction, uris->owl_someValuesFrom, NULL, NULL); | |||||
| if (sf) { | |||||
| const SordNode* type = sord_iter_get_node(sf, SORD_OBJECT); | |||||
| SordIter* v = sord_search(model, instance, prop, NULL, NULL); | |||||
| bool found = false; | |||||
| for (; !sord_iter_end(v); sord_iter_next(v)) { | |||||
| const SordNode* value = sord_iter_get_node(v, SORD_OBJECT); | |||||
| if (check_type(model, uris, value, type)) { | |||||
| found = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!found) { | |||||
| st = errorf("%s has no <%s> values of type <%s>\n", | |||||
| sord_node_get_string(instance), | |||||
| sord_node_get_string(prop), | |||||
| sord_node_get_string(type)); | |||||
| } | |||||
| sord_iter_free(v); | |||||
| } | |||||
| sord_iter_free(sf); | |||||
| return st; | |||||
| } | |||||
| static int | |||||
| check_class_instances(SordModel* model, | |||||
| const URIs* uris, | |||||
| const SordNode* restriction, | |||||
| const SordNode* klass) | |||||
| { | |||||
| // Check immediate instances of this class | |||||
| SordIter* i = sord_search(model, NULL, uris->rdf_type, klass, NULL); | |||||
| for (; !sord_iter_end(i); sord_iter_next(i)) { | |||||
| const SordNode* instance = sord_iter_get_node(i, SORD_SUBJECT); | |||||
| check_instance(model, uris, restriction, instance); | |||||
| } | |||||
| sord_iter_free(i); | |||||
| // Check instances of all subclasses recursively | |||||
| SordIter* s = sord_search(model, NULL, uris->rdfs_subClassOf, klass, NULL); | |||||
| for (; !sord_iter_end(s); sord_iter_next(s)) { | |||||
| const SordNode* subklass = sord_iter_get_node(s, SORD_SUBJECT); | |||||
| check_class_instances(model, uris, restriction, subklass); | |||||
| } | |||||
| sord_iter_free(s); | |||||
| return 0; | |||||
| } | |||||
| static int | |||||
| check_instances(SordModel* model, const URIs* uris) | |||||
| { | |||||
| int st = 0; | |||||
| SordIter* r = sord_search( | |||||
| model, NULL, uris->rdf_type, uris->owl_Restriction, NULL); | |||||
| for (; !sord_iter_end(r); sord_iter_next(r)) { | |||||
| const SordNode* restriction = sord_iter_get_node(r, SORD_SUBJECT); | |||||
| const SordNode* prop = sord_get( | |||||
| model, restriction, uris->owl_onProperty, NULL, NULL); | |||||
| if (!prop) { | |||||
| continue; | |||||
| } | |||||
| SordIter* c = sord_search( | |||||
| model, NULL, uris->rdfs_subClassOf, restriction, NULL); | |||||
| for (; !sord_iter_end(c); sord_iter_next(c)) { | |||||
| const SordNode* klass = sord_iter_get_node(c, SORD_SUBJECT); | |||||
| check_class_instances(model, uris, restriction, klass); | |||||
| } | |||||
| sord_iter_free(c); | |||||
| } | |||||
| sord_iter_free(r); | |||||
| return st; | |||||
| } | |||||
| int | |||||
| main(int argc, char** argv) | |||||
| { | |||||
| if (argc < 2) { | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| int a = 1; | |||||
| for (; a < argc && argv[a][0] == '-'; ++a) { | |||||
| if (argv[a][1] == 'l') { | |||||
| one_line_errors = true; | |||||
| } else if (argv[a][1] == 'v') { | |||||
| return print_version(); | |||||
| } else { | |||||
| fprintf(stderr, "%s: Unknown option `%s'\n", argv[0], argv[a]); | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| } | |||||
| SordWorld* world = sord_world_new(); | |||||
| SordModel* model = sord_new(world, SORD_SPO|SORD_OPS, false); | |||||
| SerdEnv* env = serd_env_new(&SERD_NODE_NULL); | |||||
| SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); | |||||
| for (; a < argc; ++a) { | |||||
| const uint8_t* input = (const uint8_t*)argv[a]; | |||||
| uint8_t* in_path = absolute_path(serd_uri_to_path(input)); | |||||
| if (!in_path) { | |||||
| fprintf(stderr, "Skipping file %s\n", input); | |||||
| continue; | |||||
| } | |||||
| SerdURI base_uri; | |||||
| SerdNode base_uri_node = serd_node_new_file_uri( | |||||
| in_path, NULL, &base_uri, false); | |||||
| serd_env_set_base_uri(env, &base_uri_node); | |||||
| const SerdStatus st = serd_reader_read_file(reader, in_path); | |||||
| if (st) { | |||||
| fprintf(stderr, "error reading %s: %s\n", | |||||
| in_path, serd_strerror(st)); | |||||
| } | |||||
| serd_node_free(&base_uri_node); | |||||
| free(in_path); | |||||
| } | |||||
| serd_reader_free(reader); | |||||
| serd_env_free(env); | |||||
| #define URI(prefix, suffix) \ | |||||
| uris.prefix##_##suffix = sord_new_uri(world, NS_##prefix #suffix) | |||||
| URIs uris; | |||||
| URI(foaf, Document); | |||||
| URI(owl, AnnotationProperty); | |||||
| URI(owl, Class); | |||||
| URI(owl, DatatypeProperty); | |||||
| URI(owl, FunctionalProperty); | |||||
| URI(owl, InverseFunctionalProperty); | |||||
| URI(owl, ObjectProperty); | |||||
| URI(owl, OntologyProperty); | |||||
| URI(owl, Restriction); | |||||
| URI(owl, Thing); | |||||
| URI(owl, cardinality); | |||||
| URI(owl, equivalentClass); | |||||
| URI(owl, minCardinality); | |||||
| URI(owl, onDatatype); | |||||
| URI(owl, onProperty); | |||||
| URI(owl, someValuesFrom); | |||||
| URI(owl, withRestrictions); | |||||
| URI(rdf, PlainLiteral); | |||||
| URI(rdf, Property); | |||||
| URI(rdf, first); | |||||
| URI(rdf, rest); | |||||
| URI(rdf, type); | |||||
| URI(rdfs, Class); | |||||
| URI(rdfs, Literal); | |||||
| URI(rdfs, Resource); | |||||
| URI(rdfs, domain); | |||||
| URI(rdfs, label); | |||||
| URI(rdfs, range); | |||||
| URI(rdfs, subClassOf); | |||||
| URI(xsd, anyURI); | |||||
| URI(xsd, decimal); | |||||
| URI(xsd, maxInclusive); | |||||
| URI(xsd, minInclusive); | |||||
| URI(xsd, pattern); | |||||
| URI(xsd, string); | |||||
| #ifndef HAVE_PCRE | |||||
| fprintf(stderr, "warning: Built without PCRE, datatypes not checked.\n"); | |||||
| #endif | |||||
| const int prop_st = check_properties(model, &uris); | |||||
| const int inst_st = check_instances(model, &uris); | |||||
| printf("Found %d errors among %d files (checked %d restrictions)\n", | |||||
| n_errors, argc - 1, n_restrictions); | |||||
| sord_free(model); | |||||
| sord_world_free(world); | |||||
| return prop_st || inst_st; | |||||
| } | |||||
| @@ -0,0 +1,220 @@ | |||||
| /* | |||||
| Copyright 2011-2013 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #define _BSD_SOURCE // for realpath | |||||
| #include <assert.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #ifdef _WIN32 | |||||
| # include <windows.h> | |||||
| #endif | |||||
| #include "serd/serd.h" | |||||
| #include "sord/sord.h" | |||||
| #include "sord_config.h" | |||||
| #define SORDI_ERROR(msg) fprintf(stderr, "sordi: " msg); | |||||
| #define SORDI_ERRORF(fmt, ...) fprintf(stderr, "sordi: " fmt, __VA_ARGS__); | |||||
| typedef struct { | |||||
| SerdWriter* writer; | |||||
| SerdEnv* env; | |||||
| SerdNode base_uri_node; | |||||
| SerdURI base_uri; | |||||
| SordModel* sord; | |||||
| } State; | |||||
| static int | |||||
| print_version(void) | |||||
| { | |||||
| printf("sordi " SORD_VERSION " <http://drobilla.net/software/sord>\n"); | |||||
| printf("Copyright 2011-2013 David Robillard <http://drobilla.net>.\n" | |||||
| "License: <http://www.opensource.org/licenses/isc>\n" | |||||
| "This is free software; you are free to change and redistribute it." | |||||
| "\nThere is NO WARRANTY, to the extent permitted by law.\n"); | |||||
| return 0; | |||||
| } | |||||
| static int | |||||
| print_usage(const char* name, bool error) | |||||
| { | |||||
| FILE* const os = error ? stderr : stdout; | |||||
| fprintf(os, "%s", error ? "\n" : ""); | |||||
| fprintf(os, "Usage: %s [OPTION]... INPUT [BASE_URI]\n", name); | |||||
| fprintf(os, "Load and re-serialise RDF data.\n"); | |||||
| fprintf(os, "Use - for INPUT to read from standard input.\n\n"); | |||||
| fprintf(os, " -h Display this help and exit\n"); | |||||
| fprintf(os, " -i SYNTAX Input syntax (`turtle' or `ntriples')\n"); | |||||
| fprintf(os, " -o SYNTAX Output syntax (`turtle' or `ntriples')\n"); | |||||
| fprintf(os, " -s INPUT Parse INPUT as string (terminates options)\n"); | |||||
| fprintf(os, " -v Display version information and exit\n"); | |||||
| return error ? 1 : 0; | |||||
| } | |||||
| static bool | |||||
| set_syntax(SerdSyntax* syntax, const char* name) | |||||
| { | |||||
| if (!strcmp(name, "turtle")) { | |||||
| *syntax = SERD_TURTLE; | |||||
| } else if (!strcmp(name, "ntriples")) { | |||||
| *syntax = SERD_NTRIPLES; | |||||
| } else { | |||||
| SORDI_ERRORF("unknown syntax `%s'\n", name); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static uint8_t* | |||||
| absolute_path(const uint8_t* path) | |||||
| { | |||||
| #ifdef _WIN32 | |||||
| char* out = (char*)malloc(MAX_PATH); | |||||
| GetFullPathName((const char*)path, MAX_PATH, out, NULL); | |||||
| return (uint8_t*)out; | |||||
| #else | |||||
| return (uint8_t*)realpath((const char*)path, NULL); | |||||
| #endif | |||||
| } | |||||
| int | |||||
| main(int argc, char** argv) | |||||
| { | |||||
| if (argc < 2) { | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| FILE* in_fd = NULL; | |||||
| SerdSyntax input_syntax = SERD_TURTLE; | |||||
| SerdSyntax output_syntax = SERD_NTRIPLES; | |||||
| bool from_file = true; | |||||
| const uint8_t* in_name = NULL; | |||||
| int a = 1; | |||||
| for (; a < argc && argv[a][0] == '-'; ++a) { | |||||
| if (argv[a][1] == '\0') { | |||||
| in_name = (const uint8_t*)"(stdin)"; | |||||
| in_fd = stdin; | |||||
| break; | |||||
| } else if (argv[a][1] == 'h') { | |||||
| return print_usage(argv[0], false); | |||||
| } else if (argv[a][1] == 'v') { | |||||
| return print_version(); | |||||
| } else if (argv[a][1] == 's') { | |||||
| in_name = (const uint8_t*)"(string)"; | |||||
| from_file = false; | |||||
| ++a; | |||||
| break; | |||||
| } else if (argv[a][1] == 'i') { | |||||
| if (++a == argc) { | |||||
| SORDI_ERROR("option requires an argument -- 'i'\n\n"); | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| if (!set_syntax(&input_syntax, argv[a])) { | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| } else if (argv[a][1] == 'o') { | |||||
| if (++a == argc) { | |||||
| SORDI_ERROR("option requires an argument -- 'o'\n\n"); | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| if (!set_syntax(&output_syntax, argv[a])) { | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| } else { | |||||
| SORDI_ERRORF("invalid option -- '%s'\n", argv[a] + 1); | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| } | |||||
| if (a == argc) { | |||||
| SORDI_ERROR("missing input\n"); | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| const uint8_t* input = (const uint8_t*)argv[a++]; | |||||
| uint8_t* in_path = NULL; | |||||
| if (from_file) { | |||||
| in_name = in_name ? in_name : input; | |||||
| if (!in_fd) { | |||||
| in_path = absolute_path(serd_uri_to_path(in_name)); | |||||
| if (!in_path || !(in_fd = fopen((const char*)in_path, "rb"))) { | |||||
| return 1; | |||||
| } | |||||
| } | |||||
| } | |||||
| SerdURI base_uri = SERD_URI_NULL; | |||||
| SerdNode base_uri_node = SERD_NODE_NULL; | |||||
| if (a < argc) { // Base URI given on command line | |||||
| base_uri_node = serd_node_new_uri_from_string( | |||||
| (const uint8_t*)argv[a], NULL, &base_uri); | |||||
| } else if (in_fd != stdin && from_file) { // Use input file URI | |||||
| base_uri_node = serd_node_new_file_uri(in_path, NULL, &base_uri, false); | |||||
| } | |||||
| free(in_path); | |||||
| if (!base_uri_node.buf) { | |||||
| SORDI_ERROR("missing base URI\n"); | |||||
| return print_usage(argv[0], true); | |||||
| } | |||||
| SordWorld* world = sord_world_new(); | |||||
| SordModel* sord = sord_new(world, SORD_SPO|SORD_OPS, false); | |||||
| SerdEnv* env = serd_env_new(&base_uri_node); | |||||
| SerdReader* reader = sord_new_reader(sord, env, input_syntax, NULL); | |||||
| const SerdStatus status = (from_file) | |||||
| ? serd_reader_read_file_handle(reader, in_fd, in_name) | |||||
| : serd_reader_read_string(reader, input); | |||||
| serd_reader_free(reader); | |||||
| SerdEnv* write_env = serd_env_new(&base_uri_node); | |||||
| int output_style = SERD_STYLE_RESOLVED; | |||||
| if (output_syntax == SERD_NTRIPLES) { | |||||
| output_style |= SERD_STYLE_ASCII; | |||||
| } else { | |||||
| output_style |= SERD_STYLE_CURIED | SERD_STYLE_ABBREVIATED; | |||||
| } | |||||
| SerdWriter* writer = serd_writer_new( | |||||
| output_syntax, | |||||
| (SerdStyle)output_style, | |||||
| write_env, &base_uri, serd_file_sink, stdout); | |||||
| // Write @prefix directives | |||||
| serd_env_foreach(env, | |||||
| (SerdPrefixSink)serd_writer_set_prefix, | |||||
| writer); | |||||
| // Write statements | |||||
| sord_write(sord, writer, NULL); | |||||
| serd_writer_finish(writer); | |||||
| serd_writer_free(writer); | |||||
| serd_env_free(env); | |||||
| serd_env_free(write_env); | |||||
| serd_node_free(&base_uri_node); | |||||
| sord_free(sord); | |||||
| sord_world_free(world); | |||||
| return (status > SERD_FAILURE) ? 1 : 0; | |||||
| } | |||||
| @@ -0,0 +1,25 @@ | |||||
| /* | |||||
| Copyright 2011 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "sord/sordmm.hpp" | |||||
| int | |||||
| main(int argc, char** argv) | |||||
| { | |||||
| Sord::World world; | |||||
| Sord::Model model(world, "http://example.org/"); | |||||
| return 0; | |||||
| } | |||||
| @@ -0,0 +1,203 @@ | |||||
| /* | |||||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include <assert.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "serd/serd.h" | |||||
| #include "sord_config.h" | |||||
| #include "sord_internal.h" | |||||
| struct SordInserterImpl { | |||||
| SordModel* model; | |||||
| SerdEnv* env; | |||||
| }; | |||||
| SordInserter* | |||||
| sord_inserter_new(SordModel* model, | |||||
| SerdEnv* env) | |||||
| { | |||||
| SordInserter* inserter = (SordInserter*)malloc(sizeof(SordInserter)); | |||||
| inserter->model = model; | |||||
| inserter->env = env; | |||||
| return inserter; | |||||
| } | |||||
| void | |||||
| sord_inserter_free(SordInserter* inserter) | |||||
| { | |||||
| free(inserter); | |||||
| } | |||||
| SerdStatus | |||||
| sord_inserter_set_base_uri(SordInserter* inserter, | |||||
| const SerdNode* uri_node) | |||||
| { | |||||
| return serd_env_set_base_uri(inserter->env, uri_node); | |||||
| } | |||||
| SerdStatus | |||||
| sord_inserter_set_prefix(SordInserter* inserter, | |||||
| const SerdNode* name, | |||||
| const SerdNode* uri_node) | |||||
| { | |||||
| return serd_env_set_prefix(inserter->env, name, uri_node); | |||||
| } | |||||
| SerdStatus | |||||
| sord_inserter_write_statement(SordInserter* inserter, | |||||
| SerdStatementFlags flags, | |||||
| const SerdNode* graph, | |||||
| const SerdNode* subject, | |||||
| const SerdNode* predicate, | |||||
| const SerdNode* object, | |||||
| const SerdNode* object_datatype, | |||||
| const SerdNode* object_lang) | |||||
| { | |||||
| SordWorld* world = sord_get_world(inserter->model); | |||||
| SerdEnv* env = inserter->env; | |||||
| SordNode* g = sord_node_from_serd_node(world, env, graph, NULL, NULL); | |||||
| SordNode* s = sord_node_from_serd_node(world, env, subject, NULL, NULL); | |||||
| SordNode* p = sord_node_from_serd_node(world, env, predicate, NULL, NULL); | |||||
| SordNode* o = sord_node_from_serd_node(world, env, object, | |||||
| object_datatype, object_lang); | |||||
| const SordQuad tup = { s, p, o, g }; | |||||
| sord_add(inserter->model, tup); | |||||
| sord_node_free(world, o); | |||||
| sord_node_free(world, p); | |||||
| sord_node_free(world, s); | |||||
| sord_node_free(world, g); | |||||
| return SERD_SUCCESS; | |||||
| } | |||||
| SORD_API | |||||
| SerdReader* | |||||
| sord_new_reader(SordModel* model, | |||||
| SerdEnv* env, | |||||
| SerdSyntax syntax, | |||||
| SordNode* graph) | |||||
| { | |||||
| SordInserter* inserter = sord_inserter_new(model, env); | |||||
| SerdReader* reader = serd_reader_new( | |||||
| syntax, inserter, (void (*)(void*))sord_inserter_free, | |||||
| (SerdBaseSink)sord_inserter_set_base_uri, | |||||
| (SerdPrefixSink)sord_inserter_set_prefix, | |||||
| (SerdStatementSink)sord_inserter_write_statement, | |||||
| NULL); | |||||
| if (graph) { | |||||
| serd_reader_set_default_graph(reader, sord_node_to_serd_node(graph)); | |||||
| } | |||||
| return reader; | |||||
| } | |||||
| static SerdStatus | |||||
| write_statement(SordModel* sord, | |||||
| SerdWriter* writer, | |||||
| SordQuad tup, | |||||
| SerdStatementFlags flags) | |||||
| { | |||||
| const SordNode* s = tup[SORD_SUBJECT]; | |||||
| const SordNode* p = tup[SORD_PREDICATE]; | |||||
| const SordNode* o = tup[SORD_OBJECT]; | |||||
| const SordNode* d = sord_node_get_datatype(o); | |||||
| const SerdNode* ss = sord_node_to_serd_node(s); | |||||
| const SerdNode* sp = sord_node_to_serd_node(p); | |||||
| const SerdNode* so = sord_node_to_serd_node(o); | |||||
| const SerdNode* sd = sord_node_to_serd_node(d); | |||||
| const char* lang_str = sord_node_get_language(o); | |||||
| size_t lang_len = lang_str ? strlen(lang_str) : 0; | |||||
| SerdNode language = SERD_NODE_NULL; | |||||
| if (lang_str) { | |||||
| language.type = SERD_LITERAL; | |||||
| language.n_bytes = lang_len; | |||||
| language.n_chars = lang_len; | |||||
| language.buf = (const uint8_t*)lang_str; | |||||
| }; | |||||
| // TODO: Subject abbreviation | |||||
| if (sord_node_is_inline_object(s) && !(flags & SERD_ANON_CONT)) { | |||||
| return SERD_SUCCESS; | |||||
| } | |||||
| SerdStatus st = SERD_SUCCESS; | |||||
| if (sord_node_is_inline_object(o)) { | |||||
| SordQuad sub_pat = { o, 0, 0, 0 }; | |||||
| SordIter* sub_iter = sord_find(sord, sub_pat); | |||||
| SerdStatementFlags start_flags = flags | |||||
| | ((sub_iter) ? SERD_ANON_O_BEGIN : SERD_EMPTY_O); | |||||
| st = serd_writer_write_statement( | |||||
| writer, start_flags, NULL, ss, sp, so, sd, &language); | |||||
| if (!st && sub_iter) { | |||||
| flags |= SERD_ANON_CONT; | |||||
| for (; !st && !sord_iter_end(sub_iter); sord_iter_next(sub_iter)) { | |||||
| SordQuad sub_tup; | |||||
| sord_iter_get(sub_iter, sub_tup); | |||||
| st = write_statement(sord, writer, sub_tup, flags); | |||||
| } | |||||
| sord_iter_free(sub_iter); | |||||
| serd_writer_end_anon(writer, so); | |||||
| } | |||||
| } else { | |||||
| st = serd_writer_write_statement( | |||||
| writer, flags, NULL, ss, sp, so, sd, &language); | |||||
| } | |||||
| return st; | |||||
| } | |||||
| bool | |||||
| sord_write(SordModel* model, | |||||
| SerdWriter* writer, | |||||
| SordNode* graph) | |||||
| { | |||||
| SordQuad pat = { 0, 0, 0, graph }; | |||||
| SordIter* iter = sord_find(model, pat); | |||||
| return sord_write_iter(iter, writer); | |||||
| } | |||||
| bool | |||||
| sord_write_iter(SordIter* iter, | |||||
| SerdWriter* writer) | |||||
| { | |||||
| if (!iter) { | |||||
| return false; | |||||
| } | |||||
| SordModel* model = (SordModel*)sord_iter_get_model(iter); | |||||
| SerdStatus st = SERD_SUCCESS; | |||||
| for (; !st && !sord_iter_end(iter); sord_iter_next(iter)) { | |||||
| SordQuad tup; | |||||
| sord_iter_get(iter, tup); | |||||
| st = write_statement(model, writer, tup, 0); | |||||
| } | |||||
| sord_iter_free(iter); | |||||
| return !st; | |||||
| } | |||||
| @@ -0,0 +1,738 @@ | |||||
| /* | |||||
| Copyright 2011-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include <assert.h> | |||||
| #include <stdint.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "zix/btree.h" | |||||
| // #define ZIX_BTREE_DEBUG 1 | |||||
| #define ZIX_BTREE_PAGE_SIZE 4096 | |||||
| #define ZIX_BTREE_NODE_SPACE (ZIX_BTREE_PAGE_SIZE - 2 * sizeof(uint16_t)) | |||||
| #define ZIX_BTREE_LEAF_VALS ((ZIX_BTREE_NODE_SPACE / sizeof(void*)) - 1) | |||||
| #define ZIX_BTREE_INODE_VALS (ZIX_BTREE_LEAF_VALS / 2) | |||||
| struct ZixBTreeImpl { | |||||
| ZixBTreeNode* root; | |||||
| ZixDestroyFunc destroy; | |||||
| ZixComparator cmp; | |||||
| void* cmp_data; | |||||
| size_t size; | |||||
| unsigned height; ///< Number of levels, i.e. root only has height 1 | |||||
| }; | |||||
| struct ZixBTreeNodeImpl { | |||||
| uint16_t is_leaf; | |||||
| uint16_t n_vals; | |||||
| // On 64-bit we rely on some padding here to get page-sized nodes | |||||
| void* vals[ZIX_BTREE_INODE_VALS]; // ZIX_BTREE_LEAF_VALS for leaves | |||||
| ZixBTreeNode* children[ZIX_BTREE_INODE_VALS + 1]; // Nonexistent for leaves | |||||
| }; | |||||
| typedef struct { | |||||
| ZixBTreeNode* node; | |||||
| unsigned index; | |||||
| } ZixBTreeIterFrame; | |||||
| struct ZixBTreeIterImpl { | |||||
| unsigned level; ///< Current level in stack | |||||
| ZixBTreeIterFrame stack[]; ///< Position stack | |||||
| }; | |||||
| #ifdef ZIX_BTREE_DEBUG | |||||
| ZIX_PRIVATE void | |||||
| print_node(const ZixBTreeNode* n, const char* prefix) | |||||
| { | |||||
| printf("%s[", prefix); | |||||
| for (uint16_t v = 0; v < n->n_vals; ++v) { | |||||
| printf(" %lu", (uintptr_t)n->vals[v]); | |||||
| } | |||||
| printf(" ]\n"); | |||||
| } | |||||
| ZIX_PRIVATE void | |||||
| print_tree(const ZixBTreeNode* parent, const ZixBTreeNode* node, int level) | |||||
| { | |||||
| if (node) { | |||||
| if (!parent) { | |||||
| printf("TREE {\n"); | |||||
| } | |||||
| for (int i = 0; i < level + 1; ++i) { | |||||
| printf(" "); | |||||
| } | |||||
| print_node(node, ""); | |||||
| if (!node->is_leaf) { | |||||
| for (uint16_t i = 0; i < node->n_vals + 1; ++i) { | |||||
| print_tree(node, node->children[i], level + 1); | |||||
| } | |||||
| } | |||||
| if (!parent) { | |||||
| printf("}\n"); | |||||
| } | |||||
| } | |||||
| } | |||||
| #endif // ZIX_BTREE_DEBUG | |||||
| ZIX_PRIVATE ZixBTreeNode* | |||||
| zix_btree_node_new(const bool leaf) | |||||
| { | |||||
| assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE); | |||||
| ZixBTreeNode* node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode)); | |||||
| if (node) { | |||||
| node->is_leaf = leaf; | |||||
| node->n_vals = 0; | |||||
| } | |||||
| return node; | |||||
| } | |||||
| ZIX_API ZixBTree* | |||||
| zix_btree_new(const ZixComparator cmp, | |||||
| void* const cmp_data, | |||||
| const ZixDestroyFunc destroy) | |||||
| { | |||||
| ZixBTree* t = (ZixBTree*)malloc(sizeof(ZixBTree)); | |||||
| if (t) { | |||||
| t->root = zix_btree_node_new(true); | |||||
| t->destroy = destroy; | |||||
| t->cmp = cmp; | |||||
| t->cmp_data = cmp_data; | |||||
| t->size = 0; | |||||
| t->height = 1; | |||||
| if (!t->root) { | |||||
| free(t); | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| return t; | |||||
| } | |||||
| ZIX_PRIVATE void | |||||
| zix_btree_free_rec(ZixBTree* const t, ZixBTreeNode* const n) | |||||
| { | |||||
| if (n) { | |||||
| if (t->destroy) { | |||||
| for (uint16_t i = 0; i < n->n_vals; ++i) { | |||||
| t->destroy(n->vals[i]); | |||||
| } | |||||
| } | |||||
| if (!n->is_leaf) { | |||||
| for (uint16_t i = 0; i < n->n_vals + 1; ++i) { | |||||
| zix_btree_free_rec(t, n->children[i]); | |||||
| } | |||||
| } | |||||
| free(n); | |||||
| } | |||||
| } | |||||
| ZIX_API void | |||||
| zix_btree_free(ZixBTree* const t) | |||||
| { | |||||
| if (t) { | |||||
| zix_btree_free_rec(t, t->root); | |||||
| free(t); | |||||
| } | |||||
| } | |||||
| ZIX_API size_t | |||||
| zix_btree_size(const ZixBTree* const t) | |||||
| { | |||||
| return t->size; | |||||
| } | |||||
| ZIX_PRIVATE uint16_t | |||||
| zix_btree_max_vals(const ZixBTreeNode* const node) | |||||
| { | |||||
| return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS; | |||||
| } | |||||
| ZIX_PRIVATE uint16_t | |||||
| zix_btree_min_vals(const ZixBTreeNode* const node) | |||||
| { | |||||
| return ((zix_btree_max_vals(node) + 1) / 2) - 1; | |||||
| } | |||||
| /** Shift pointers in `array` of length `n` right starting at `i`. */ | |||||
| ZIX_PRIVATE void | |||||
| zix_btree_ainsert(void** const array, | |||||
| const uint16_t n, | |||||
| const uint16_t i, | |||||
| void* const e) | |||||
| { | |||||
| memmove(array + i + 1, array + i, (n - i) * sizeof(e)); | |||||
| array[i] = e; | |||||
| } | |||||
| /** Erase element `i` in `array` of length `n` and return erased element. */ | |||||
| ZIX_PRIVATE void* | |||||
| zix_btree_aerase(void** const array, const uint16_t n, const uint16_t i) | |||||
| { | |||||
| void* const ret = array[i]; | |||||
| memmove(array + i, array + i + 1, (n - i) * sizeof(ret)); | |||||
| return ret; | |||||
| } | |||||
| /** Split lhs, the i'th child of `n`, into two nodes. */ | |||||
| ZIX_PRIVATE ZixBTreeNode* | |||||
| zix_btree_split_child(ZixBTreeNode* const n, | |||||
| const uint16_t i, | |||||
| ZixBTreeNode* const lhs) | |||||
| { | |||||
| assert(lhs->n_vals == zix_btree_max_vals(lhs)); | |||||
| assert(n->n_vals < ZIX_BTREE_INODE_VALS); | |||||
| assert(i < n->n_vals + 1); | |||||
| assert(n->children[i] == lhs); | |||||
| const uint16_t max_n_vals = zix_btree_max_vals(lhs); | |||||
| ZixBTreeNode* rhs = zix_btree_node_new(lhs->is_leaf); | |||||
| if (!rhs) { | |||||
| return NULL; | |||||
| } | |||||
| // LHS and RHS get roughly half, less the middle value which moves up | |||||
| lhs->n_vals = max_n_vals / 2; | |||||
| rhs->n_vals = max_n_vals - lhs->n_vals - 1; | |||||
| // Copy large half of values from LHS to new RHS node | |||||
| memcpy(rhs->vals, | |||||
| lhs->vals + lhs->n_vals + 1, | |||||
| rhs->n_vals * sizeof(void*)); | |||||
| // Copy large half of children from LHS to new RHS node | |||||
| if (!lhs->is_leaf) { | |||||
| memcpy(rhs->children, | |||||
| lhs->children + lhs->n_vals + 1, | |||||
| (rhs->n_vals + 1) * sizeof(ZixBTreeNode*)); | |||||
| } | |||||
| // Move middle value up to parent | |||||
| zix_btree_ainsert(n->vals, n->n_vals, i, lhs->vals[lhs->n_vals]); | |||||
| // Insert new RHS node in parent at position i | |||||
| zix_btree_ainsert((void**)n->children, ++n->n_vals, i + 1, rhs); | |||||
| return rhs; | |||||
| } | |||||
| /** Find the first value in `n` that is not less than `e` (lower bound). */ | |||||
| ZIX_PRIVATE uint16_t | |||||
| zix_btree_node_find(const ZixBTree* const t, | |||||
| const ZixBTreeNode* const n, | |||||
| const void* const e, | |||||
| bool* const equal) | |||||
| { | |||||
| uint16_t first = 0; | |||||
| uint16_t len = n->n_vals; | |||||
| while (len > 0) { | |||||
| const uint16_t half = len >> 1; | |||||
| const uint16_t i = first + half; | |||||
| const int cmp = t->cmp(n->vals[i], e, t->cmp_data); | |||||
| if (cmp == 0) { | |||||
| *equal = true; | |||||
| len = half; // Keep searching for wildcard matches | |||||
| } else if (cmp < 0) { | |||||
| const uint16_t chop = half + 1; | |||||
| first += chop; | |||||
| len -= chop; | |||||
| } else { | |||||
| len = half; | |||||
| } | |||||
| } | |||||
| assert(!*equal || t->cmp(n->vals[first], e, t->cmp_data) == 0); | |||||
| return first; | |||||
| } | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_insert(ZixBTree* const t, void* const e) | |||||
| { | |||||
| ZixBTreeNode* parent = NULL; // Parent of n | |||||
| ZixBTreeNode* n = t->root; // Current node | |||||
| uint16_t i = 0; // Index of n in parent | |||||
| while (n) { | |||||
| if (n->n_vals == zix_btree_max_vals(n)) { | |||||
| // Node is full, split to ensure there is space for a leaf split | |||||
| if (!parent) { | |||||
| // Root is full, grow tree upwards | |||||
| if (!(parent = zix_btree_node_new(false))) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| t->root = parent; | |||||
| parent->children[0] = n; | |||||
| ++t->height; | |||||
| } | |||||
| ZixBTreeNode* const rhs = zix_btree_split_child(parent, i, n); | |||||
| if (!rhs) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| const int cmp = t->cmp(parent->vals[i], e, t->cmp_data); | |||||
| if (cmp == 0) { | |||||
| return ZIX_STATUS_EXISTS; | |||||
| } else if (cmp < 0) { | |||||
| // Move to new RHS | |||||
| n = rhs; | |||||
| ++i; | |||||
| } | |||||
| } | |||||
| assert(!parent || parent->children[i] == n); | |||||
| bool equal = false; | |||||
| i = zix_btree_node_find(t, n, e, &equal); | |||||
| if (equal) { | |||||
| return ZIX_STATUS_EXISTS; | |||||
| } else if (!n->is_leaf) { | |||||
| // Descend to child node left of value | |||||
| parent = n; | |||||
| n = n->children[i]; | |||||
| } else { | |||||
| // Insert into internal node | |||||
| zix_btree_ainsert(n->vals, n->n_vals++, i, e); | |||||
| break; | |||||
| } | |||||
| } | |||||
| ++t->size; | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } | |||||
| ZIX_PRIVATE ZixBTreeIter* | |||||
| zix_btree_iter_new(const ZixBTree* const t) | |||||
| { | |||||
| const size_t s = t->height * sizeof(ZixBTreeIterFrame); | |||||
| ZixBTreeIter* const i = (ZixBTreeIter*)malloc(sizeof(ZixBTreeIter) + s); | |||||
| if (i) { | |||||
| i->level = 0; | |||||
| } | |||||
| return i; | |||||
| } | |||||
| ZIX_PRIVATE void | |||||
| zix_btree_iter_set_frame(ZixBTreeIter* const ti, | |||||
| ZixBTreeNode* const n, | |||||
| const uint16_t i) | |||||
| { | |||||
| if (ti) { | |||||
| ti->stack[ti->level].node = n; | |||||
| ti->stack[ti->level].index = i; | |||||
| } | |||||
| } | |||||
| ZIX_PRIVATE bool | |||||
| zix_btree_node_is_minimal(ZixBTreeNode* const n) | |||||
| { | |||||
| assert(n->n_vals >= zix_btree_min_vals(n)); | |||||
| return n->n_vals == zix_btree_min_vals(n); | |||||
| } | |||||
| /** Enlarge left child by stealing a value from its right sibling. */ | |||||
| ZIX_PRIVATE ZixBTreeNode* | |||||
| zix_btree_rotate_left(ZixBTreeNode* const parent, const uint16_t i) | |||||
| { | |||||
| ZixBTreeNode* const lhs = parent->children[i]; | |||||
| ZixBTreeNode* const rhs = parent->children[i + 1]; | |||||
| // Move parent value to end of LHS | |||||
| lhs->vals[lhs->n_vals++] = parent->vals[i]; | |||||
| // Move first child pointer from RHS to end of LHS | |||||
| if (!lhs->is_leaf) { | |||||
| lhs->children[lhs->n_vals] = (ZixBTreeNode*)zix_btree_aerase( | |||||
| (void**)rhs->children, rhs->n_vals, 0); | |||||
| } | |||||
| // Move first value in RHS to parent | |||||
| parent->vals[i] = zix_btree_aerase(rhs->vals, --rhs->n_vals, 0); | |||||
| return lhs; | |||||
| } | |||||
| /** Enlarge right child by stealing a value from its left sibling. */ | |||||
| ZIX_PRIVATE ZixBTreeNode* | |||||
| zix_btree_rotate_right(ZixBTreeNode* const parent, const uint16_t i) | |||||
| { | |||||
| ZixBTreeNode* const lhs = parent->children[i - 1]; | |||||
| ZixBTreeNode* const rhs = parent->children[i]; | |||||
| // Prepend parent value to RHS | |||||
| zix_btree_ainsert(rhs->vals, rhs->n_vals++, 0, parent->vals[i - 1]); | |||||
| // Move last child pointer from LHS and prepend to RHS | |||||
| if (!lhs->is_leaf) { | |||||
| zix_btree_ainsert((void**)rhs->children, | |||||
| rhs->n_vals, | |||||
| 0, | |||||
| lhs->children[lhs->n_vals]); | |||||
| } | |||||
| // Move last value from LHS to parent | |||||
| parent->vals[i - 1] = lhs->vals[--lhs->n_vals]; | |||||
| return rhs; | |||||
| } | |||||
| /** Move n[i] down, merge the left and right child, return the merged node. */ | |||||
| ZIX_PRIVATE ZixBTreeNode* | |||||
| zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const uint16_t i) | |||||
| { | |||||
| ZixBTreeNode* const lhs = n->children[i]; | |||||
| ZixBTreeNode* const rhs = n->children[i + 1]; | |||||
| assert(zix_btree_node_is_minimal(n->children[i])); | |||||
| assert(lhs->n_vals + rhs->n_vals < zix_btree_max_vals(lhs)); | |||||
| // Move parent value to end of LHS | |||||
| lhs->vals[lhs->n_vals++] = zix_btree_aerase(n->vals, n->n_vals, i); | |||||
| // Erase corresponding child pointer (to RHS) in parent | |||||
| zix_btree_aerase((void**)n->children, n->n_vals, i + 1); | |||||
| // Add everything from RHS to end of LHS | |||||
| memcpy(lhs->vals + lhs->n_vals, rhs->vals, rhs->n_vals * sizeof(void*)); | |||||
| if (!lhs->is_leaf) { | |||||
| memcpy(lhs->children + lhs->n_vals, | |||||
| rhs->children, | |||||
| (rhs->n_vals + 1) * sizeof(void*)); | |||||
| } | |||||
| lhs->n_vals += rhs->n_vals; | |||||
| if (--n->n_vals == 0) { | |||||
| // Root is now empty, replace it with its only child | |||||
| assert(n == t->root); | |||||
| t->root = lhs; | |||||
| free(n); | |||||
| } | |||||
| free(rhs); | |||||
| return lhs; | |||||
| } | |||||
| /** Remove and return the min value from the subtree rooted at `n`. */ | |||||
| ZIX_PRIVATE void* | |||||
| zix_btree_remove_min(ZixBTree* const t, ZixBTreeNode* n) | |||||
| { | |||||
| while (!n->is_leaf) { | |||||
| if (zix_btree_node_is_minimal(n->children[0])) { | |||||
| // Leftmost child is minimal, must expand | |||||
| if (!zix_btree_node_is_minimal(n->children[1])) { | |||||
| // Child's right sibling has at least one key to steal | |||||
| n = zix_btree_rotate_left(n, 0); | |||||
| } else { | |||||
| // Both child and right sibling are minimal, merge | |||||
| n = zix_btree_merge(t, n, 0); | |||||
| } | |||||
| } else { | |||||
| n = n->children[0]; | |||||
| } | |||||
| } | |||||
| return zix_btree_aerase(n->vals, --n->n_vals, 0); | |||||
| } | |||||
| /** Remove and return the max value from the subtree rooted at `n`. */ | |||||
| ZIX_PRIVATE void* | |||||
| zix_btree_remove_max(ZixBTree* const t, ZixBTreeNode* n) | |||||
| { | |||||
| while (!n->is_leaf) { | |||||
| if (zix_btree_node_is_minimal(n->children[n->n_vals])) { | |||||
| // Leftmost child is minimal, must expand | |||||
| if (!zix_btree_node_is_minimal(n->children[n->n_vals - 1])) { | |||||
| // Child's left sibling has at least one key to steal | |||||
| n = zix_btree_rotate_right(n, n->n_vals); | |||||
| } else { | |||||
| // Both child and left sibling are minimal, merge | |||||
| n = zix_btree_merge(t, n, n->n_vals - 1); | |||||
| } | |||||
| } else { | |||||
| n = n->children[n->n_vals]; | |||||
| } | |||||
| } | |||||
| return n->vals[--n->n_vals]; | |||||
| } | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_remove(ZixBTree* const t, | |||||
| const void* const e, | |||||
| void** const out, | |||||
| ZixBTreeIter** const next) | |||||
| { | |||||
| ZixBTreeNode* n = t->root; | |||||
| ZixBTreeIter* ti = NULL; | |||||
| const bool user_iter = next && *next; | |||||
| if (next) { | |||||
| if (!*next && !(*next = zix_btree_iter_new(t))) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| ti = *next; | |||||
| ti->level = 0; | |||||
| } | |||||
| while (true) { | |||||
| /* To remove in a single walk down, the tree is adjusted along the way | |||||
| so that the current node always has at least one more value than the | |||||
| minimum required in general. Thus, there is always room to remove | |||||
| without adjusting on the way back up. */ | |||||
| assert(n == t->root || !zix_btree_node_is_minimal(n)); | |||||
| bool equal = false; | |||||
| const uint16_t i = zix_btree_node_find(t, n, e, &equal); | |||||
| zix_btree_iter_set_frame(ti, n, i); | |||||
| if (n->is_leaf) { | |||||
| if (equal) { | |||||
| // Found in leaf node | |||||
| *out = zix_btree_aerase(n->vals, --n->n_vals, i); | |||||
| if (ti && i == n->n_vals) { | |||||
| if (i == 0) { | |||||
| ti->stack[ti->level = 0].node = NULL; | |||||
| } else { | |||||
| --ti->stack[ti->level].index; | |||||
| zix_btree_iter_increment(ti); | |||||
| } | |||||
| } | |||||
| --t->size; | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } else { | |||||
| // Not found in leaf node, or tree | |||||
| if (ti && !user_iter) { | |||||
| zix_btree_iter_free(ti); | |||||
| *next = NULL; | |||||
| } | |||||
| return ZIX_STATUS_NOT_FOUND; | |||||
| } | |||||
| } else if (equal) { | |||||
| // Found in internal node | |||||
| if (!zix_btree_node_is_minimal(n->children[i])) { | |||||
| // Left child can remove without merge | |||||
| *out = n->vals[i]; | |||||
| n->vals[i] = zix_btree_remove_max(t, n->children[i]); | |||||
| --t->size; | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } else if (!zix_btree_node_is_minimal(n->children[i + 1])) { | |||||
| // Right child can remove without merge | |||||
| *out = n->vals[i]; | |||||
| n->vals[i] = zix_btree_remove_min(t, n->children[i + 1]); | |||||
| --t->size; | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } else { | |||||
| // Both preceding and succeeding child are minimal | |||||
| n = zix_btree_merge(t, n, i); | |||||
| } | |||||
| } else { | |||||
| // Not found in internal node, key is in/under children[i] | |||||
| if (zix_btree_node_is_minimal(n->children[i])) { | |||||
| if (i > 0 && !zix_btree_node_is_minimal(n->children[i - 1])) { | |||||
| // Steal a key from child's left sibling | |||||
| n = zix_btree_rotate_right(n, i); | |||||
| } else if (i < n->n_vals && | |||||
| !zix_btree_node_is_minimal(n->children[i + 1])) { | |||||
| // Steal a key from child's right sibling | |||||
| n = zix_btree_rotate_left(n, i); | |||||
| } else { | |||||
| // Both child's siblings are minimal, merge them | |||||
| if (i < n->n_vals) { | |||||
| n = zix_btree_merge(t, n, i); | |||||
| } else { | |||||
| n = zix_btree_merge(t, n, i - 1); | |||||
| if (ti) { | |||||
| --ti->stack[ti->level].index; | |||||
| } | |||||
| } | |||||
| } | |||||
| } else { | |||||
| n = n->children[i]; | |||||
| } | |||||
| } | |||||
| if (ti) { | |||||
| ++ti->level; | |||||
| } | |||||
| } | |||||
| assert(false); // Not reached | |||||
| return ZIX_STATUS_ERROR; | |||||
| } | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_find(const ZixBTree* const t, | |||||
| const void* const e, | |||||
| ZixBTreeIter** const ti) | |||||
| { | |||||
| ZixBTreeNode* n = t->root; | |||||
| if (!(*ti = zix_btree_iter_new(t))) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| while (n) { | |||||
| bool equal = false; | |||||
| const uint16_t i = zix_btree_node_find(t, n, e, &equal); | |||||
| zix_btree_iter_set_frame(*ti, n, i); | |||||
| if (equal) { | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } else if (n->is_leaf) { | |||||
| break; | |||||
| } else { | |||||
| ++(*ti)->level; | |||||
| n = n->children[i]; | |||||
| } | |||||
| } | |||||
| zix_btree_iter_free(*ti); | |||||
| *ti = NULL; | |||||
| return ZIX_STATUS_NOT_FOUND; | |||||
| } | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_lower_bound(const ZixBTree* const t, | |||||
| const void* const e, | |||||
| ZixBTreeIter** const ti) | |||||
| { | |||||
| if (!t) { | |||||
| *ti = NULL; | |||||
| return ZIX_STATUS_BAD_ARG; | |||||
| } | |||||
| ZixBTreeNode* n = t->root; | |||||
| bool found = false; | |||||
| unsigned found_level = 0; | |||||
| if (!(*ti = zix_btree_iter_new(t))) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| while (n) { | |||||
| bool equal = false; | |||||
| const uint16_t i = zix_btree_node_find(t, n, e, &equal); | |||||
| zix_btree_iter_set_frame(*ti, n, i); | |||||
| if (equal) { | |||||
| found_level = (*ti)->level; | |||||
| found = true; | |||||
| } | |||||
| if (n->is_leaf) { | |||||
| break; | |||||
| } else { | |||||
| ++(*ti)->level; | |||||
| n = n->children[i]; | |||||
| assert(n); | |||||
| } | |||||
| } | |||||
| const ZixBTreeIterFrame* const frame = &(*ti)->stack[(*ti)->level]; | |||||
| if (frame->index == frame->node->n_vals) { | |||||
| if (found) { | |||||
| // Found on a previous level but went too far | |||||
| (*ti)->level = found_level; | |||||
| } else { | |||||
| // Reached end (key is greater than everything in tree) | |||||
| (*ti)->stack[0].node = NULL; | |||||
| } | |||||
| } | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } | |||||
| ZIX_API void* | |||||
| zix_btree_get(const ZixBTreeIter* const ti) | |||||
| { | |||||
| const ZixBTreeIterFrame* const frame = &ti->stack[ti->level]; | |||||
| assert(frame->index < frame->node->n_vals); | |||||
| return frame->node->vals[frame->index]; | |||||
| } | |||||
| ZIX_API ZixBTreeIter* | |||||
| zix_btree_begin(const ZixBTree* const t) | |||||
| { | |||||
| ZixBTreeIter* const i = zix_btree_iter_new(t); | |||||
| if (!i) { | |||||
| return NULL; | |||||
| } else if (t->size == 0) { | |||||
| i->stack[0].node = NULL; | |||||
| } else { | |||||
| ZixBTreeNode* n = t->root; | |||||
| i->stack[0].node = n; | |||||
| i->stack[0].index = 0; | |||||
| while (!n->is_leaf) { | |||||
| n = n->children[0]; | |||||
| ++i->level; | |||||
| i->stack[i->level].node = n; | |||||
| i->stack[i->level].index = 0; | |||||
| } | |||||
| } | |||||
| return i; | |||||
| } | |||||
| ZIX_API bool | |||||
| zix_btree_iter_is_end(const ZixBTreeIter* const i) | |||||
| { | |||||
| return !i || i->stack[0].node == NULL; | |||||
| } | |||||
| ZIX_API void | |||||
| zix_btree_iter_increment(ZixBTreeIter* const i) | |||||
| { | |||||
| ZixBTreeIterFrame* f = &i->stack[i->level]; | |||||
| if (f->node->is_leaf) { | |||||
| // Leaf, move right | |||||
| assert(f->index < f->node->n_vals); | |||||
| if (++f->index == f->node->n_vals) { | |||||
| // Reached end of leaf, move up | |||||
| f = &i->stack[i->level]; | |||||
| while (i->level > 0 && f->index == f->node->n_vals) { | |||||
| f = &i->stack[--i->level]; | |||||
| assert(f->index <= f->node->n_vals); | |||||
| } | |||||
| if (f->index == f->node->n_vals) { | |||||
| // Reached end of tree | |||||
| assert(i->level == 0); | |||||
| f->node = NULL; | |||||
| f->index = 0; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| // Internal node, move down to next child | |||||
| assert(f->index < f->node->n_vals); | |||||
| ZixBTreeNode* child = f->node->children[++f->index]; | |||||
| f = &i->stack[++i->level]; | |||||
| f->node = child; | |||||
| f->index = 0; | |||||
| // Move down and left until we hit a leaf | |||||
| while (!f->node->is_leaf) { | |||||
| child = f->node->children[0]; | |||||
| f = &i->stack[++i->level]; | |||||
| f->node = child; | |||||
| f->index = 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| ZIX_API void | |||||
| zix_btree_iter_free(ZixBTreeIter* const i) | |||||
| { | |||||
| free(i); | |||||
| } | |||||
| @@ -0,0 +1,151 @@ | |||||
| /* | |||||
| Copyright 2011-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef ZIX_BTREE_H | |||||
| #define ZIX_BTREE_H | |||||
| #include <stddef.h> | |||||
| #include "zix/common.h" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| /** | |||||
| @addtogroup zix | |||||
| @{ | |||||
| @name BTree | |||||
| @{ | |||||
| */ | |||||
| /** | |||||
| A B-Tree. | |||||
| */ | |||||
| typedef struct ZixBTreeImpl ZixBTree; | |||||
| /** | |||||
| A B-Tree node (opaque). | |||||
| */ | |||||
| typedef struct ZixBTreeNodeImpl ZixBTreeNode; | |||||
| /** | |||||
| An iterator over a B-Tree. | |||||
| Note that modifying the trees invalidates all iterators, so all iterators | |||||
| are const iterators. | |||||
| */ | |||||
| typedef struct ZixBTreeIterImpl ZixBTreeIter; | |||||
| /** | |||||
| Create a new (empty) B-Tree. | |||||
| */ | |||||
| ZIX_API ZixBTree* | |||||
| zix_btree_new(ZixComparator cmp, | |||||
| void* cmp_data, | |||||
| ZixDestroyFunc destroy); | |||||
| /** | |||||
| Free `t`. | |||||
| */ | |||||
| ZIX_API void | |||||
| zix_btree_free(ZixBTree* t); | |||||
| /** | |||||
| Return the number of elements in `t`. | |||||
| */ | |||||
| ZIX_API size_t | |||||
| zix_btree_size(const ZixBTree* t); | |||||
| /** | |||||
| Insert the element `e` into `t`. | |||||
| */ | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_insert(ZixBTree* t, void* e); | |||||
| /** | |||||
| Remove the value `e` from `t`. | |||||
| @param out Set to point to the removed pointer (which may not equal `e`). | |||||
| @param next If non-NULL, pointed to the value following `e`. If *next is | |||||
| also non-NULL, the iterator is reused, otherwise a new one is allocated. To | |||||
| reuse an iterator, no items may have been added since its creation. | |||||
| */ | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_remove(ZixBTree* t, const void* e, void** out, ZixBTreeIter** next); | |||||
| /** | |||||
| Set `ti` to an element equal to `e` in `t`. | |||||
| If no such item exists, `ti` is set to NULL. | |||||
| */ | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_find(const ZixBTree* t, const void* e, ZixBTreeIter** ti); | |||||
| /** | |||||
| Set `ti` to the smallest element in `t` that is not less than `e`. | |||||
| Wildcards are supported, so if the search key `e` compares equal to many | |||||
| values in the tree, `ti` will be set to the least such element. The search | |||||
| key `e` is always passed as the second argument to the comparator. | |||||
| */ | |||||
| ZIX_API ZixStatus | |||||
| zix_btree_lower_bound(const ZixBTree* t, const void* e, ZixBTreeIter** ti); | |||||
| /** | |||||
| Return the data associated with the given tree item. | |||||
| */ | |||||
| ZIX_API void* | |||||
| zix_btree_get(const ZixBTreeIter* ti); | |||||
| /** | |||||
| Return an iterator to the first (smallest) element in `t`. | |||||
| The returned iterator must be freed with zix_btree_iter_free(). | |||||
| */ | |||||
| ZIX_API ZixBTreeIter* | |||||
| zix_btree_begin(const ZixBTree* t); | |||||
| /** | |||||
| Return true iff `i` is an iterator to the end of its tree. | |||||
| */ | |||||
| ZIX_API bool | |||||
| zix_btree_iter_is_end(const ZixBTreeIter* i); | |||||
| /** | |||||
| Increment `i` to point to the next element in the tree. | |||||
| */ | |||||
| ZIX_API void | |||||
| zix_btree_iter_increment(ZixBTreeIter* i); | |||||
| /** | |||||
| Free `i`. | |||||
| */ | |||||
| ZIX_API void | |||||
| zix_btree_iter_free(ZixBTreeIter* i); | |||||
| /** | |||||
| @} | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* ZIX_BTREE_H */ | |||||
| @@ -0,0 +1,88 @@ | |||||
| /* | |||||
| Copyright 2011 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef ZIX_COMMON_H | |||||
| #define ZIX_COMMON_H | |||||
| /** | |||||
| @addtogroup zix | |||||
| @{ | |||||
| */ | |||||
| /** @cond */ | |||||
| #ifdef ZIX_SHARED | |||||
| # ifdef _WIN32 | |||||
| # define ZIX_LIB_IMPORT __declspec(dllimport) | |||||
| # define ZIX_LIB_EXPORT __declspec(dllexport) | |||||
| # else | |||||
| # define ZIX_LIB_IMPORT __attribute__((visibility("default"))) | |||||
| # define ZIX_LIB_EXPORT __attribute__((visibility("default"))) | |||||
| # endif | |||||
| # ifdef ZIX_INTERNAL | |||||
| # define ZIX_API ZIX_LIB_EXPORT | |||||
| # else | |||||
| # define ZIX_API ZIX_LIB_IMPORT | |||||
| # endif | |||||
| # define ZIX_PRIVATE static | |||||
| #elif defined(ZIX_INLINE) | |||||
| # define ZIX_API static inline | |||||
| # define ZIX_PRIVATE static inline | |||||
| #else | |||||
| # define ZIX_API | |||||
| # define ZIX_PRIVATE static | |||||
| #endif | |||||
| /** @endcond */ | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #else | |||||
| # include <stdbool.h> | |||||
| #endif | |||||
| typedef enum { | |||||
| ZIX_STATUS_SUCCESS, | |||||
| ZIX_STATUS_ERROR, | |||||
| ZIX_STATUS_NO_MEM, | |||||
| ZIX_STATUS_NOT_FOUND, | |||||
| ZIX_STATUS_EXISTS, | |||||
| ZIX_STATUS_BAD_ARG, | |||||
| ZIX_STATUS_BAD_PERMS, | |||||
| } ZixStatus; | |||||
| /** | |||||
| Function for comparing two elements. | |||||
| */ | |||||
| typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); | |||||
| /** | |||||
| Function for testing equality of two elements. | |||||
| */ | |||||
| typedef bool (*ZixEqualFunc)(const void* a, const void* b); | |||||
| /** | |||||
| Function to destroy an element. | |||||
| */ | |||||
| typedef void (*ZixDestroyFunc)(void* ptr); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* ZIX_COMMON_H */ | |||||
| @@ -0,0 +1,57 @@ | |||||
| /* | |||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include "zix/digest.h" | |||||
| #ifdef __SSE4_2__ | |||||
| # include <smmintrin.h> | |||||
| #endif | |||||
| ZIX_API uint32_t | |||||
| zix_digest_start(void) | |||||
| { | |||||
| #ifdef __SSE4_2__ | |||||
| return 1; // CRC32 initial value | |||||
| #else | |||||
| return 5381; // DJB hash initial value | |||||
| #endif | |||||
| } | |||||
| ZIX_API uint32_t | |||||
| zix_digest_add(uint32_t hash, const void* const buf, const size_t len) | |||||
| { | |||||
| const uint8_t* str = (const uint8_t*)buf; | |||||
| #ifdef __SSE4_2__ | |||||
| // SSE 4.2 CRC32 | |||||
| for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) { | |||||
| hash = _mm_crc32_u32(hash, *(const uint32_t*)str); | |||||
| str += sizeof(uint32_t); | |||||
| } | |||||
| if (len & sizeof(uint16_t)) { | |||||
| hash = _mm_crc32_u16(hash, *(const uint16_t*)str); | |||||
| str += sizeof(uint16_t); | |||||
| } | |||||
| if (len & sizeof(uint8_t)) { | |||||
| hash = _mm_crc32_u8(hash, *(const uint8_t*)str); | |||||
| } | |||||
| #else | |||||
| // Classic DJB hash | |||||
| for (size_t i = 0; i < len; ++i) { | |||||
| hash = (hash << 5) + hash + str[i]; | |||||
| } | |||||
| #endif | |||||
| return hash; | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| /* | |||||
| Copyright 2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef ZIX_DIGEST_H | |||||
| #define ZIX_DIGEST_H | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| #include "zix/common.h" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| ZIX_API uint32_t | |||||
| zix_digest_start(void); | |||||
| ZIX_API uint32_t | |||||
| zix_digest_add(uint32_t hash, const void* buf, const size_t len); | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* ZIX_DIGEST_H */ | |||||
| @@ -0,0 +1,232 @@ | |||||
| /* | |||||
| Copyright 2011-2014 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #include <assert.h> | |||||
| #include <stdint.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "zix/hash.h" | |||||
| /** | |||||
| Primes, each slightly less than twice its predecessor, and as far away | |||||
| from powers of two as possible. | |||||
| */ | |||||
| static const unsigned sizes[] = { | |||||
| 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, | |||||
| 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, | |||||
| 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, 0 | |||||
| }; | |||||
| typedef struct ZixHashEntry { | |||||
| struct ZixHashEntry* next; ///< Next entry in bucket | |||||
| uint32_t hash; ///< Non-modulo hash value | |||||
| // Value follows here (access with zix_hash_value) | |||||
| } ZixHashEntry; | |||||
| struct ZixHashImpl { | |||||
| ZixHashFunc hash_func; | |||||
| ZixEqualFunc equal_func; | |||||
| ZixHashEntry** buckets; | |||||
| const unsigned* n_buckets; | |||||
| size_t value_size; | |||||
| unsigned count; | |||||
| }; | |||||
| static inline void* | |||||
| zix_hash_value(ZixHashEntry* entry) | |||||
| { | |||||
| return entry + 1; | |||||
| } | |||||
| ZIX_API ZixHash* | |||||
| zix_hash_new(ZixHashFunc hash_func, | |||||
| ZixEqualFunc equal_func, | |||||
| size_t value_size) | |||||
| { | |||||
| ZixHash* hash = (ZixHash*)malloc(sizeof(ZixHash)); | |||||
| if (hash) { | |||||
| hash->hash_func = hash_func; | |||||
| hash->equal_func = equal_func; | |||||
| hash->n_buckets = &sizes[0]; | |||||
| hash->value_size = value_size; | |||||
| hash->count = 0; | |||||
| if (!(hash->buckets = (ZixHashEntry**)calloc(*hash->n_buckets, | |||||
| sizeof(ZixHashEntry*)))) { | |||||
| free(hash); | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| return hash; | |||||
| } | |||||
| ZIX_API void | |||||
| zix_hash_free(ZixHash* hash) | |||||
| { | |||||
| for (unsigned b = 0; b < *hash->n_buckets; ++b) { | |||||
| ZixHashEntry* bucket = hash->buckets[b]; | |||||
| for (ZixHashEntry* e = bucket; e;) { | |||||
| ZixHashEntry* next = e->next; | |||||
| free(e); | |||||
| e = next; | |||||
| } | |||||
| } | |||||
| free(hash->buckets); | |||||
| free(hash); | |||||
| } | |||||
| ZIX_API size_t | |||||
| zix_hash_size(const ZixHash* hash) | |||||
| { | |||||
| return hash->count; | |||||
| } | |||||
| static inline void | |||||
| insert_entry(ZixHashEntry** bucket, ZixHashEntry* entry) | |||||
| { | |||||
| entry->next = *bucket; | |||||
| *bucket = entry; | |||||
| } | |||||
| static inline ZixStatus | |||||
| rehash(ZixHash* hash, unsigned new_n_buckets) | |||||
| { | |||||
| ZixHashEntry** new_buckets = (ZixHashEntry**)calloc( | |||||
| new_n_buckets, sizeof(ZixHashEntry*)); | |||||
| if (!new_buckets) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| const unsigned old_n_buckets = *hash->n_buckets; | |||||
| for (unsigned b = 0; b < old_n_buckets; ++b) { | |||||
| for (ZixHashEntry* e = hash->buckets[b]; e;) { | |||||
| ZixHashEntry* const next = e->next; | |||||
| const unsigned h = e->hash % new_n_buckets; | |||||
| insert_entry(&new_buckets[h], e); | |||||
| e = next; | |||||
| } | |||||
| } | |||||
| free(hash->buckets); | |||||
| hash->buckets = new_buckets; | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } | |||||
| static inline ZixHashEntry* | |||||
| find_entry(const ZixHash* hash, | |||||
| const void* key, | |||||
| const unsigned h, | |||||
| const unsigned h_nomod) | |||||
| { | |||||
| for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) { | |||||
| if (e->hash == h_nomod && hash->equal_func(zix_hash_value(e), key)) { | |||||
| return e; | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| ZIX_API const void* | |||||
| zix_hash_find(const ZixHash* hash, const void* value) | |||||
| { | |||||
| const unsigned h_nomod = hash->hash_func(value); | |||||
| const unsigned h = h_nomod % *hash->n_buckets; | |||||
| ZixHashEntry* const entry = find_entry(hash, value, h, h_nomod); | |||||
| return entry ? zix_hash_value(entry) : 0; | |||||
| } | |||||
| ZIX_API ZixStatus | |||||
| zix_hash_insert(ZixHash* hash, const void* value, const void** inserted) | |||||
| { | |||||
| unsigned h_nomod = hash->hash_func(value); | |||||
| unsigned h = h_nomod % *hash->n_buckets; | |||||
| ZixHashEntry* elem = find_entry(hash, value, h, h_nomod); | |||||
| if (elem) { | |||||
| assert(elem->hash == h_nomod); | |||||
| if (inserted) { | |||||
| *inserted = zix_hash_value(elem); | |||||
| } | |||||
| return ZIX_STATUS_EXISTS; | |||||
| } | |||||
| elem = (ZixHashEntry*)malloc(sizeof(ZixHashEntry) + hash->value_size); | |||||
| if (!elem) { | |||||
| return ZIX_STATUS_NO_MEM; | |||||
| } | |||||
| elem->next = NULL; | |||||
| elem->hash = h_nomod; | |||||
| memcpy(elem + 1, value, hash->value_size); | |||||
| const unsigned next_n_buckets = *(hash->n_buckets + 1); | |||||
| if (next_n_buckets != 0 && (hash->count + 1) >= next_n_buckets) { | |||||
| if (!rehash(hash, next_n_buckets)) { | |||||
| h = h_nomod % *(++hash->n_buckets); | |||||
| } | |||||
| } | |||||
| insert_entry(&hash->buckets[h], elem); | |||||
| ++hash->count; | |||||
| if (inserted) { | |||||
| *inserted = zix_hash_value(elem); | |||||
| } | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } | |||||
| ZIX_API ZixStatus | |||||
| zix_hash_remove(ZixHash* hash, const void* value) | |||||
| { | |||||
| const unsigned h_nomod = hash->hash_func(value); | |||||
| const unsigned h = h_nomod % *hash->n_buckets; | |||||
| ZixHashEntry** next_ptr = &hash->buckets[h]; | |||||
| for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) { | |||||
| if (h_nomod == e->hash && | |||||
| hash->equal_func(zix_hash_value(e), value)) { | |||||
| *next_ptr = e->next; | |||||
| free(e); | |||||
| return ZIX_STATUS_SUCCESS; | |||||
| } | |||||
| next_ptr = &e->next; | |||||
| } | |||||
| if (hash->n_buckets != sizes) { | |||||
| const unsigned prev_n_buckets = *(hash->n_buckets - 1); | |||||
| if (hash->count - 1 <= prev_n_buckets) { | |||||
| if (!rehash(hash, prev_n_buckets)) { | |||||
| --hash->n_buckets; | |||||
| } | |||||
| } | |||||
| } | |||||
| --hash->count; | |||||
| return ZIX_STATUS_NOT_FOUND; | |||||
| } | |||||
| ZIX_API void | |||||
| zix_hash_foreach(ZixHash* hash, | |||||
| ZixHashVisitFunc f, | |||||
| void* user_data) | |||||
| { | |||||
| for (unsigned b = 0; b < *hash->n_buckets; ++b) { | |||||
| ZixHashEntry* bucket = hash->buckets[b]; | |||||
| for (ZixHashEntry* e = bucket; e; e = e->next) { | |||||
| f(zix_hash_value(e), user_data); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,140 @@ | |||||
| /* | |||||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||||
| Permission to use, copy, modify, and/or distribute this software for any | |||||
| purpose with or without fee is hereby granted, provided that the above | |||||
| copyright notice and this permission notice appear in all copies. | |||||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef ZIX_HASH_H | |||||
| #define ZIX_HASH_H | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| #include "zix/common.h" | |||||
| #ifdef __cplusplus | |||||
| extern "C" { | |||||
| #endif | |||||
| /** | |||||
| @addtogroup zix | |||||
| @{ | |||||
| @name Hash | |||||
| @{ | |||||
| */ | |||||
| typedef struct ZixHashImpl ZixHash; | |||||
| /** | |||||
| Function for computing the hash of an element. | |||||
| */ | |||||
| typedef uint32_t (*ZixHashFunc)(const void* value); | |||||
| /** | |||||
| Function to visit a hash element. | |||||
| */ | |||||
| typedef void (*ZixHashVisitFunc)(void* value, | |||||
| void* user_data); | |||||
| /** | |||||
| Create a new hash table. | |||||
| To minimize space overhead, unlike many hash tables this stores a single | |||||
| value, not a key and a value. Any size of value can be stored, but all the | |||||
| values in the hash table must be the same size, and the values must be safe | |||||
| to copy with memcpy. To get key:value behaviour, simply insert a struct | |||||
| with a key and value into the hash. | |||||
| @param hash_func The hashing function. | |||||
| @param equal_func A function to test value equality. | |||||
| @param value_size The size of the values to be stored. | |||||
| */ | |||||
| ZIX_API ZixHash* | |||||
| zix_hash_new(ZixHashFunc hash_func, | |||||
| ZixEqualFunc equal_func, | |||||
| size_t value_size); | |||||
| /** | |||||
| Free `hash`. | |||||
| */ | |||||
| ZIX_API void | |||||
| zix_hash_free(ZixHash* hash); | |||||
| /** | |||||
| Return the number of elements in `hash`. | |||||
| */ | |||||
| ZIX_API size_t | |||||
| zix_hash_size(const ZixHash* hash); | |||||
| /** | |||||
| Insert an item into `hash`. | |||||
| If no matching value is found, ZIX_STATUS_SUCCESS will be returned, and @p | |||||
| inserted will be pointed to the copy of `value` made in the new hash node. | |||||
| If a matching value already exists, ZIX_STATUS_EXISTS will be returned, and | |||||
| `inserted` will be pointed to the existing value. | |||||
| @param hash The hash table. | |||||
| @param value The value to be inserted. | |||||
| @param inserted The copy of `value` in the hash table. | |||||
| @return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS, or ZIX_STATUS_NO_MEM. | |||||
| */ | |||||
| ZIX_API ZixStatus | |||||
| zix_hash_insert(ZixHash* hash, | |||||
| const void* value, | |||||
| const void** inserted); | |||||
| /** | |||||
| Remove an item from `hash`. | |||||
| @param hash The hash table. | |||||
| @param value The value to remove. | |||||
| @return ZIX_STATUS_SUCCES or ZIX_STATUS_NOT_FOUND. | |||||
| */ | |||||
| ZIX_API ZixStatus | |||||
| zix_hash_remove(ZixHash* hash, | |||||
| const void* value); | |||||
| /** | |||||
| Search for an item in `hash`. | |||||
| @param hash The hash table. | |||||
| @param value The value to search for. | |||||
| */ | |||||
| ZIX_API const void* | |||||
| zix_hash_find(const ZixHash* hash, | |||||
| const void* value); | |||||
| /** | |||||
| Call `f` on each value in `hash`. | |||||
| @param hash The hash table. | |||||
| @param f The function to call on each value. | |||||
| @param user_data The user_data parameter passed to `f`. | |||||
| */ | |||||
| ZIX_API void | |||||
| zix_hash_foreach(ZixHash* hash, | |||||
| ZixHashVisitFunc f, | |||||
| void* user_data); | |||||
| /** | |||||
| @} | |||||
| @} | |||||
| */ | |||||
| #ifdef __cplusplus | |||||
| } /* extern "C" */ | |||||
| #endif | |||||
| #endif /* ZIX_HASH_H */ | |||||
| @@ -0,0 +1,20 @@ | |||||
| These are the tests for the Turtle Terse RDF Triple Language | |||||
| that must be passed by conformant systems. See | |||||
| http://www.dajobe.org/2004/01/turtle/ | |||||
| for the full conformance information. | |||||
| The format is a set of good tests and bad tests. | |||||
| Good tests are a pair of files: | |||||
| abc.ttl abc.out | |||||
| which are the input Turtle file and the expected output RDF triples, | |||||
| written in N-Triples. | |||||
| bad tests are of the form | |||||
| bad-XX.ttl | |||||
| which must fail. | |||||
| The tests should be performed with an assumed base URI | |||||
| of http://www.w3.org/2001/sw/DataAccess/df1/tests/ | |||||
| Dave | |||||
| @@ -0,0 +1,126 @@ | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates membership of a class" . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#label> "type"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#label> "type"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#comment> "The concept of Class" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#label> "Class"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#label> "Classe"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> <http://www.w3.org/2000/01/rdf-schema#comment> "Properties used to express RDF Schema constraints." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> <http://www.w3.org/2000/01/rdf-schema#label> "ConstraintProperty"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> <http://www.w3.org/2000/01/rdf-schema#label> "Propri\u00E9t\u00E9Contrainte"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> <http://www.w3.org/2000/01/rdf-schema#comment> "Resources used to express RDF Schema constraints." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> <http://www.w3.org/2000/01/rdf-schema#label> "ConstraintResource"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> <http://www.w3.org/2000/01/rdf-schema#label> "RessourceContrainte"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ConstraintResource> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#comment> "This represents the set Containers." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#label> "Container"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#label> "Enveloppe"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/2000/01/rdf-schema#label> "ContainerMembershipProperty"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/2000/01/rdf-schema#comment> "This represents the set of atomic values, eg. textual strings." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/2000/01/rdf-schema#label> "Literal"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/2000/01/rdf-schema#label> "Litt\u00E9ral"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/2000/01/rdf-schema#comment> "The most general class" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/2000/01/rdf-schema#label> "Resource"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/2000/01/rdf-schema#label> "Ressource"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#comment> "Use this for descriptions" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#label> "comment"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#label> "commentaire"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Literal> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#comment> "This is how we associate a class with properties that its instances can have" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#label> "domain"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#label> "domaine"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates a resource containing and defining the subject resource." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#label> "esD\u00E9finiPar"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#label> "isDefinedBy"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#seeAlso> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#comment> "Provides a human-readable version of a resource name." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#label> "label"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#label> "label"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Literal> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#ConstraintProperty> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#comment> "Properties that can be used in a schema to provide constraints" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#label> "range"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#label> "\u00E9tendue"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates a resource that provides information about the subject resource." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#label> "seeAlso"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#label> "voirAussi"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates membership of a class" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#label> "sousClasseDe"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#label> "subClassOf"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates specialization of properties" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#label> "sousPropri\u00E9t\u00E9De"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#label> "subPropertyOf"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#label> "Alt"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#label> "Choix"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#label> "Bag"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#label> "Ensemble"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#comment> "The concept of a property." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#label> "Property"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#label> "Propri\u00E9t\u00E9"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#label> "Sequence"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#label> "S\u00E9quence"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#comment> "This represents the set of reified statements." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#label> "D\u00E9claration"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#label> "Statement"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#label> "object"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#label> "objet"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#label> "predicate"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#label> "pr\u00E9dicat"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#label> "subject"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#label> "sujet"@fr . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/2000/01/rdf-schema#label> "object"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/2000/01/rdf-schema#label> "value"@fr . | |||||
| <http://www.w3.org/2000/01/rdf-schema#> <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema-more> . | |||||
| @@ -0,0 +1,36 @@ | |||||
| _:genid1 <http://jena.hpl.hp.com/2003/03/result-set#variable> "x" . | |||||
| _:genid1 <http://jena.hpl.hp.com/2003/03/result-set#value> "123"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| _:genid2 <http://jena.hpl.hp.com/2003/03/result-set#variable> "y" . | |||||
| _:genid2 <http://jena.hpl.hp.com/2003/03/result-set#value> <http://example.com/resource1> . | |||||
| _:genid3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://jena.hpl.hp.com/2003/03/result-set#ResultSolution> . | |||||
| _:genid3 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid1 . | |||||
| _:genid3 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid2 . | |||||
| _:genid4 <http://jena.hpl.hp.com/2003/03/result-set#variable> "x" . | |||||
| _:genid4 <http://jena.hpl.hp.com/2003/03/result-set#value> "2003-01-21" . | |||||
| _:genid5 <http://jena.hpl.hp.com/2003/03/result-set#variable> "y" . | |||||
| _:genid5 <http://jena.hpl.hp.com/2003/03/result-set#value> <http://example.com/resource2> . | |||||
| _:genid6 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://jena.hpl.hp.com/2003/03/result-set#ResultSolution> . | |||||
| _:genid6 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid4 . | |||||
| _:genid6 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid5 . | |||||
| _:genid7 <http://jena.hpl.hp.com/2003/03/result-set#variable> "x" . | |||||
| _:genid7 <http://jena.hpl.hp.com/2003/03/result-set#value> "anon1" . | |||||
| _:genid8 <http://jena.hpl.hp.com/2003/03/result-set#variable> "y" . | |||||
| _:genid8 <http://jena.hpl.hp.com/2003/03/result-set#value> _:a . | |||||
| _:genid9 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://jena.hpl.hp.com/2003/03/result-set#ResultSolution> . | |||||
| _:genid9 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid7 . | |||||
| _:genid9 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid8 . | |||||
| _:genid10 <http://jena.hpl.hp.com/2003/03/result-set#variable> "x" . | |||||
| _:genid10 <http://jena.hpl.hp.com/2003/03/result-set#value> "anon2" . | |||||
| _:genid11 <http://jena.hpl.hp.com/2003/03/result-set#variable> "y" . | |||||
| _:genid11 <http://jena.hpl.hp.com/2003/03/result-set#value> _:a . | |||||
| _:genid12 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://jena.hpl.hp.com/2003/03/result-set#ResultSolution> . | |||||
| _:genid12 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid10 . | |||||
| _:genid12 <http://jena.hpl.hp.com/2003/03/result-set#binding> _:genid11 . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://jena.hpl.hp.com/2003/03/result-set#ResultSet> . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#size> "4"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#resultVariable> "x" . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#resultVariable> "y" . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#solution> _:genid3 . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#solution> _:genid6 . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#solution> _:genid9 . | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/rdfq-results.ttl> <http://jena.hpl.hp.com/2003/03/result-set#solution> _:genid12 . | |||||
| @@ -0,0 +1,131 @@ | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates membership of a class" . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#label> "type"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#comment> "A collection of alternatives."@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#label> "Alt"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Alt> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#comment> "An unordered collection."@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#label> "Bag"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#comment> "The concept of a property." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#label> "Property"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#comment> "An ordered collection."@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#label> "Seq"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#comment> "The class of RDF statements." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#label> "Statement"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#comment> "The object of an RDF statement." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> <http://www.w3.org/2000/01/rdf-schema#label> "object"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#comment> "the predicate of an RDF statement." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#label> "predicate"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#comment> "The subject of an RDF statement." . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#label> "subject"@en . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/2000/01/rdf-schema#comment> "Identifies the principal value (usually a string) of a property when the property value is a structured resource" . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||||
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#value> <http://www.w3.org/2000/01/rdf-schema#label> "value"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#> <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema-more> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#comment> "The concept of Class" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#label> "Class"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Class> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#comment> "This represents the set Containers." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#label> "Container"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Container> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/2000/01/rdf-schema#comment> "The container membership properties, rdf:1, rdf:2, ..., all of which are sub-properties of 'member'." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/2000/01/rdf-schema#label> "ContainerMembershipProperty"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#ContainerMembershipProperty> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/2000/01/rdf-schema#comment> "This represents the set of atomic values, eg. textual strings." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Literal> <http://www.w3.org/2000/01/rdf-schema#label> "Literal"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/2000/01/rdf-schema#comment> "The class resource, everything." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#Resource> <http://www.w3.org/2000/01/rdf-schema#label> "Resource"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#comment> "Use this for descriptions" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#label> "comment"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#comment> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Literal> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#comment> "A domain class for a property type" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#label> "domain"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates the namespace of a resource" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#label> "isDefinedBy"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#seeAlso> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#comment> "Provides a human-readable version of a resource name." . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#label> "label"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#label> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Literal> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#member> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#member> <http://www.w3.org/2000/01/rdf-schema#comment> "a member of a container" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#member> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Container> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#member> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#member> <http://www.w3.org/2000/01/rdf-schema#label> "member"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#comment> "A range class for a property type" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#label> "range"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#comment> "A resource that provides information about the subject resource" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#label> "seeAlso"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Resource> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates membership of a class" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#label> "subClassOf"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/2000/01/rdf-schema#Class> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#comment> "Indicates specialization of properties" . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#domain> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#isDefinedBy> <http://www.w3.org/2000/01/rdf-schema#> . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#label> "subPropertyOf"@en . | |||||
| <http://www.w3.org/2000/01/rdf-schema#subPropertyOf> <http://www.w3.org/2000/01/rdf-schema#range> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> . | |||||
| @@ -0,0 +1 @@ | |||||
| _:b1 <http://www.w3.org/2001/sw/DataAccess/df1/tests/test-00.ttl#x> <http://www.w3.org/2001/sw/DataAccess/df1/tests/test-00.ttl#y> . | |||||
| @@ -0,0 +1,3 @@ | |||||
| <http://example.org/base1#a> <http://example.org/base1#b> <http://example.org/base1#c> . | |||||
| <http://example.org/base2#a> <http://example.org/base2#b> <http://example.org/base2#c> . | |||||
| <http://example.org/base1#a> <http://example.org/base2#a> <http://example.org/base3#a> . | |||||
| @@ -0,0 +1,3 @@ | |||||
| <http://example.org/base#a> <http://example.org/base#b> <http://example.org/base#c> . | |||||
| <http://example.org/base#a> <http://example.org/base#b> <http://example.org/base#d> . | |||||
| <http://example.org/base#a> <http://example.org/base#b> <http://example.org/base#e> . | |||||
| @@ -0,0 +1,3 @@ | |||||
| <http://example.org/base#a> <http://example.org/base#b> <http://example.org/base#c> . | |||||
| <http://example.org/base#a> <http://example.org/base#d> <http://example.org/base#e> . | |||||
| <http://example.org/base#a> <http://example.org/base#f> <http://example.org/base#g> . | |||||
| @@ -0,0 +1,2 @@ | |||||
| _:b1 <http://example.org/base#a> <http://example.org/base#b> . | |||||
| <http://example.org/base#c> <http://example.org/base#d> _:b2 . | |||||
| @@ -0,0 +1,4 @@ | |||||
| _:b1 <http://example.org/base#a> <http://example.org/base#b> . | |||||
| _:b1 <http://example.org/base#c> <http://example.org/base#d> . | |||||
| <http://example.org/base#e> <http://example.org/base#f> _:b2 . | |||||
| _:b2 <http://example.org/base#g> <http://example.org/base#h> . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/base#a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/base#b> . | |||||
| @@ -0,0 +1,5 @@ | |||||
| <http://example.org/stuff/1.0/a> <http://example.org/stuff/1.0/b> _:b1 . | |||||
| _:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "apple" . | |||||
| _:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b2 . | |||||
| _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "banana" . | |||||
| _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/stuff/1.0/a> <http://example.org/stuff/1.0/b> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> . | |||||
| @@ -0,0 +1,4 @@ | |||||
| _:hasParent <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#ObjectProperty> . | |||||
| _:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction> . | |||||
| _:b1 <http://www.w3.org/2002/07/owl#onProperty> _:hasParent . | |||||
| _:b1 <http://www.w3.org/2002/07/owl#maxCardinality> "2"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| @@ -0,0 +1,5 @@ | |||||
| <http://example.org/res1> <http://example.org/prop1> "000000"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://example.org/res2> <http://example.org/prop2> "0"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://example.org/res3> <http://example.org/prop3> "000001"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://example.org/res4> <http://example.org/prop4> "2"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://example.org/res5> <http://example.org/prop5> "4"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| @@ -0,0 +1,4 @@ | |||||
| <http://example.org/ex1#foo-bar> <http://example.org/ex1#foo_bar> "a" . | |||||
| <http://example.org/ex2#foo-bar> <http://example.org/ex2#foo_bar> "b" . | |||||
| <http://example.org/ex3#foo-bar> <http://example.org/ex3#foo_bar> "c" . | |||||
| <http://example.org/ex4#foo-bar> <http://example.org/ex4#foo_bar> "d" . | |||||
| @@ -0,0 +1,4 @@ | |||||
| <http://example.org/ex#foo> <http://www.w3.org/1999/02/22-rdf-syntax-ns#_1> "1" . | |||||
| <http://example.org/ex#foo> <http://www.w3.org/1999/02/22-rdf-syntax-ns#_2> "2" . | |||||
| <http://example.org/ex#foo> <http://example.org/myprop#_abc> "def" . | |||||
| <http://example.org/ex#foo> <http://example.org/myprop#_345> "678" . | |||||
| @@ -0,0 +1,2 @@ | |||||
| _:b1 <http://example.org/ron> _:b2 . | |||||
| <http://example.org/ron> <http://example.org/ron> <http://example.org/ron> . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/ex#a> <http://example.org/ex#b> "a long\n\tliteral\nwith\nnewlines" . | |||||
| @@ -0,0 +1,2 @@ | |||||
| <http://example.org/foo#a> <http://example.org/foo#b> "\nthis \ris a \U00015678long\t\nliteral\uABCD\n" . | |||||
| <http://example.org/foo#d> <http://example.org/foo#e> "\tThis \uABCDis\r \U00015678another\n\none\n" . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/#a> <http://example.org/#b> "1.0"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| @@ -0,0 +1,2 @@ | |||||
| <http://example.org/#a> <http://example.org/#b> "" . | |||||
| <http://example.org/#c> <http://example.org/#d> "" . | |||||
| @@ -0,0 +1,3 @@ | |||||
| <http://example.org#a> <http://example.org#b> "1.0"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org#c> <http://example.org#d> "1"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://example.org#e> <http://example.org#f> "1.0e0"^^<http://www.w3.org/2001/XMLSchema#double> . | |||||
| @@ -0,0 +1,3 @@ | |||||
| <http://example.org#a> <http://example.org#b> "-1.0"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org#c> <http://example.org#d> "-1"^^<http://www.w3.org/2001/XMLSchema#integer> . | |||||
| <http://example.org#e> <http://example.org#f> "-1.0e0"^^<http://www.w3.org/2001/XMLSchema#double> . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/ex#a> <http://example.org/ex#b> "John said: \"Hello World!\"" . | |||||
| @@ -0,0 +1,2 @@ | |||||
| <http://example.org#a> <http://example.org#b> "true"^^<http://www.w3.org/2001/XMLSchema#boolean> . | |||||
| <http://example.org#c> <http://example.org#d> "false"^^<http://www.w3.org/2001/XMLSchema#boolean> . | |||||
| @@ -0,0 +1,7 @@ | |||||
| <http://example.org/#a> <http://example.org/#b> <http://example.org/#c> . | |||||
| <http://example.org/#d> <http://example.org/#e> <http://example.org/#f> . | |||||
| <http://example.org/#g> <http://example.org/#h> <http://example.org/#i> . | |||||
| <http://example.org/#g> <http://example.org/#h> <http://example.org/#j> . | |||||
| <http://example.org/#k> <http://example.org/#l> <http://example.org/#m> . | |||||
| <http://example.org/#k> <http://example.org/#n> <http://example.org/#o> . | |||||
| <http://example.org/#k> <http://example.org/#p> <http://example.org/#q> . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/#a> <http://example.org/#b> <http://example.org/#c> . | |||||
| @@ -0,0 +1 @@ | |||||
| <http://example.org/bar#blah> <http://example.org/bar#blah> <http://example.org/bar#blah> . | |||||
| @@ -0,0 +1,5 @@ | |||||
| <http://www.w3.org/2001/sw/DataAccess/df1/tests/a1> <http://www.w3.org/2001/sw/DataAccess/df1/tests/b1> <http://www.w3.org/2001/sw/DataAccess/df1/tests/c1> . | |||||
| <http://example.org/ns/a2> <http://example.org/ns/b2> <http://example.org/ns/c2> . | |||||
| <http://example.org/ns/foo/a3> <http://example.org/ns/foo/b3> <http://example.org/ns/foo/c3> . | |||||
| <http://example.org/ns/foo/bar#a4> <http://example.org/ns/foo/bar#b4> <http://example.org/ns/foo/bar#c4> . | |||||
| <http://example.org/ns2#a5> <http://example.org/ns2#b5> <http://example.org/ns2#c5> . | |||||
| @@ -0,0 +1,2 @@ | |||||
| <http://example.org/base#c> <http://example.org/base#d> _:b1 . | |||||
| _:B1 <http://example.org/base#a> <http://example.org/base#b> . | |||||
| @@ -0,0 +1,5 @@ | |||||
| <http://example.org/test-lang#thing> <http://example.org/test-lang#greeting> "Hello"@en . | |||||
| <http://example.org/test-lang#thing> <http://example.org/test-lang#greeting> "Howdy"@en-us . | |||||
| <http://example.org/test-lang#thing> <http://example.org/test-lang#greeting> "Bonjour"@fr . | |||||
| <http://example.org/test-lang#thing> <http://example.org/test-lang#greeting> "Guten Tag"@de-latn-de . | |||||
| <http://example.org/test-lang#thing> <http://example.org/test-lang#greeting> "HEY MAN"@en-crazy0place . | |||||
| @@ -0,0 +1,10 @@ | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "0.1"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "+0.2"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "-0.3"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> ".4"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "+.5"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "-.6"^^<http://www.w3.org/2001/XMLSchema#decimal> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "1.58490e-05"^^<http://www.w3.org/2001/XMLSchema#double> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "1.58490e+05"^^<http://www.w3.org/2001/XMLSchema#double> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "1.58490e05"^^<http://www.w3.org/2001/XMLSchema#double> . | |||||
| <http://example.org/eg#thing> <http://example.org/eg#num> "1.58490E05"^^<http://www.w3.org/2001/XMLSchema#double> . | |||||
| @@ -0,0 +1,334 @@ | |||||
| #!/usr/bin/env python | |||||
| import glob | |||||
| import os | |||||
| import subprocess | |||||
| import sys | |||||
| import waflib.Logs as Logs | |||||
| import waflib.Options as Options | |||||
| import waflib.extras.autowaf as autowaf | |||||
| # Library and package version (UNIX style major, minor, micro) | |||||
| # major increment <=> incompatible changes | |||||
| # minor increment <=> compatible changes (additions) | |||||
| # micro increment <=> no interface changes | |||||
| SORD_VERSION = '0.14.0' | |||||
| SORD_MAJOR_VERSION = '0' | |||||
| # Mandatory waf variables | |||||
| APPNAME = 'sord' # Package name for waf dist | |||||
| VERSION = SORD_VERSION # Package version for waf dist | |||||
| top = '.' # Source directory | |||||
| out = 'build' # Build directory | |||||
| def options(opt): | |||||
| opt.load('compiler_c') | |||||
| opt.load('compiler_cxx') | |||||
| autowaf.set_options(opt) | |||||
| opt.add_option('--no-utils', action='store_true', dest='no_utils', | |||||
| help='Do not build command line utilities') | |||||
| opt.add_option('--test', action='store_true', dest='build_tests', | |||||
| help='Build unit tests') | |||||
| opt.add_option('--no-coverage', action='store_true', dest='no_coverage', | |||||
| help='Do not use gcov for code coverage') | |||||
| opt.add_option('--static', action='store_true', dest='static', | |||||
| help='Build static library') | |||||
| opt.add_option('--no-shared', action='store_true', dest='no_shared', | |||||
| help='Do not build shared library') | |||||
| opt.add_option('--static-progs', action='store_true', dest='static_progs', | |||||
| help='Build programs as static binaries') | |||||
| opt.add_option('--dump', type='string', default='', dest='dump', | |||||
| help='Dump debugging output (iter, search, write, all)') | |||||
| def configure(conf): | |||||
| conf.load('compiler_c') | |||||
| if Options.options.build_tests: | |||||
| try: | |||||
| conf.load('compiler_cxx') | |||||
| except: | |||||
| Logs.warn("No C++ compiler, sordmm.hpp compile test skipped") | |||||
| pass | |||||
| autowaf.configure(conf) | |||||
| autowaf.set_c99_mode(conf) | |||||
| autowaf.display_header('Sord configuration') | |||||
| conf.env.BUILD_TESTS = Options.options.build_tests | |||||
| conf.env.BUILD_UTILS = not Options.options.no_utils | |||||
| conf.env.BUILD_SHARED = not Options.options.no_shared | |||||
| conf.env.STATIC_PROGS = Options.options.static_progs | |||||
| conf.env.BUILD_STATIC = (Options.options.static or | |||||
| Options.options.static_progs) | |||||
| if conf.env.BUILD_TESTS and not Options.options.no_coverage: | |||||
| conf.check_cc(lib='gcov', define_name='HAVE_GCOV', mandatory=False) | |||||
| autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', | |||||
| atleast_version='0.18.0', mandatory=True) | |||||
| autowaf.check_pkg(conf, 'libpcre', uselib_store='PCRE', mandatory=False) | |||||
| if conf.env.HAVE_PCRE: | |||||
| if conf.check(cflags=['-pthread'], mandatory=False): | |||||
| conf.env.PTHREAD_CFLAGS = ['-pthread'] | |||||
| conf.env.PTHREAD_LINKFLAGS = ['-pthread'] | |||||
| elif conf.check(linkflags=['-lpthread'], mandatory=False): | |||||
| conf.env.PTHREAD_CFLAGS = [] | |||||
| conf.env.PTHREAD_LINKFLAGS = ['-lpthread'] | |||||
| else: | |||||
| conf.env.PTHREAD_CFLAGS = [] | |||||
| conf.env.PTHREAD_LINKFLAGS = [] | |||||
| # Parse dump options and define things accordingly | |||||
| dump = Options.options.dump.split(',') | |||||
| all = 'all' in dump | |||||
| if all or 'iter' in dump: | |||||
| autowaf.define(conf, 'SORD_DEBUG_ITER', 1) | |||||
| if all or 'search' in dump: | |||||
| autowaf.define(conf, 'SORD_DEBUG_SEARCH', 1) | |||||
| if all or 'write' in dump: | |||||
| autowaf.define(conf, 'SORD_DEBUG_WRITE', 1) | |||||
| autowaf.define(conf, 'SORD_VERSION', SORD_VERSION) | |||||
| autowaf.set_lib_env(conf, 'sord', SORD_VERSION) | |||||
| conf.write_config_header('sord_config.h', remove=False) | |||||
| autowaf.display_msg(conf, 'Utilities', bool(conf.env.BUILD_UTILS)) | |||||
| autowaf.display_msg(conf, 'Unit tests', bool(conf.env.BUILD_TESTS)) | |||||
| autowaf.display_msg(conf, 'Debug dumping', dump) | |||||
| print('') | |||||
| def build(bld): | |||||
| # C/C++ Headers | |||||
| includedir = '${INCLUDEDIR}/sord-%s/sord' % SORD_MAJOR_VERSION | |||||
| bld.install_files(includedir, bld.path.ant_glob('sord/*.h')) | |||||
| bld.install_files(includedir, bld.path.ant_glob('sord/*.hpp')) | |||||
| # Pkgconfig file | |||||
| autowaf.build_pc(bld, 'SORD', SORD_VERSION, SORD_MAJOR_VERSION, 'SERD', | |||||
| {'SORD_MAJOR_VERSION' : SORD_MAJOR_VERSION}) | |||||
| source = 'src/sord.c src/syntax.c' | |||||
| libflags = ['-fvisibility=hidden'] | |||||
| libs = ['m'] | |||||
| defines = [] | |||||
| if bld.env.MSVC_COMPILER: | |||||
| libflags = [] | |||||
| libs = [] | |||||
| defines = ['snprintf=_snprintf'] | |||||
| # Shared Library | |||||
| if bld.env.BUILD_SHARED: | |||||
| obj = bld(features = 'c cshlib', | |||||
| source = source, | |||||
| includes = ['.', './src'], | |||||
| export_includes = ['.'], | |||||
| name = 'libsord', | |||||
| target = 'sord-%s' % SORD_MAJOR_VERSION, | |||||
| vnum = SORD_VERSION, | |||||
| install_path = '${LIBDIR}', | |||||
| libs = libs, | |||||
| defines = defines + ['SORD_SHARED', 'SORD_INTERNAL'], | |||||
| cflags = libflags) | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| # Static Library | |||||
| if bld.env.BUILD_STATIC: | |||||
| obj = bld(features = 'c cstlib', | |||||
| source = source, | |||||
| includes = ['.', './src'], | |||||
| export_includes = ['.'], | |||||
| name = 'libsord_static', | |||||
| target = 'sord-%s' % SORD_MAJOR_VERSION, | |||||
| vnum = SORD_VERSION, | |||||
| install_path = '${LIBDIR}', | |||||
| libs = libs, | |||||
| defines = ['SORD_INTERNAL']) | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| if bld.env.BUILD_TESTS: | |||||
| test_libs = libs | |||||
| test_cflags = [''] | |||||
| if bld.is_defined('HAVE_GCOV'): | |||||
| test_libs += ['gcov'] | |||||
| test_cflags += ['-fprofile-arcs', '-ftest-coverage'] | |||||
| # Profiled static library for test coverage | |||||
| obj = bld(features = 'c cstlib', | |||||
| source = source, | |||||
| includes = ['.', './src'], | |||||
| name = 'libsord_profiled', | |||||
| target = 'sord_profiled', | |||||
| install_path = '', | |||||
| defines = defines, | |||||
| cflags = test_cflags, | |||||
| lib = test_libs) | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| # Unit test program | |||||
| obj = bld(features = 'c cprogram', | |||||
| source = 'src/sord_test.c', | |||||
| includes = ['.', './src'], | |||||
| use = 'libsord_profiled', | |||||
| lib = test_libs, | |||||
| target = 'sord_test', | |||||
| install_path = '', | |||||
| defines = defines, | |||||
| cflags = test_cflags) | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| # Static profiled sordi for tests | |||||
| obj = bld(features = 'c cprogram', | |||||
| source = 'src/sordi.c', | |||||
| includes = ['.', './src'], | |||||
| use = 'libsord_profiled', | |||||
| lib = test_libs, | |||||
| target = 'sordi_static', | |||||
| install_path = '', | |||||
| defines = defines, | |||||
| cflags = test_cflags) | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| # C++ build test | |||||
| if bld.env.COMPILER_CXX: | |||||
| obj = bld(features = 'cxx cxxprogram', | |||||
| source = 'src/sordmm_test.cpp', | |||||
| includes = ['.', './src'], | |||||
| use = 'libsord_profiled', | |||||
| lib = test_libs, | |||||
| target = 'sordmm_test', | |||||
| install_path = '', | |||||
| defines = defines) | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| # Utilities | |||||
| if bld.env.BUILD_UTILS: | |||||
| utils = ['sordi'] | |||||
| if bld.env.HAVE_PCRE: | |||||
| utils += ['sord_validate'] | |||||
| for i in utils: | |||||
| obj = bld(features = 'c cprogram', | |||||
| source = 'src/%s.c' % i, | |||||
| includes = ['.', './src'], | |||||
| use = 'libsord', | |||||
| lib = libs, | |||||
| target = i, | |||||
| install_path = '${BINDIR}', | |||||
| defines = defines) | |||||
| if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS: | |||||
| obj.use = 'libsord_static' | |||||
| if bld.env.STATIC_PROGS: | |||||
| obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER | |||||
| obj.linkflags = ['-static', '-Wl,--start-group'] | |||||
| autowaf.use_lib(bld, obj, 'SERD') | |||||
| if i == 'sord_validate': | |||||
| autowaf.use_lib(bld, obj, 'PCRE') | |||||
| obj.cflags = bld.env.PTHREAD_CFLAGS | |||||
| obj.linkflags = bld.env.PTHREAD_LINKFLAGS | |||||
| # Documentation | |||||
| autowaf.build_dox(bld, 'SORD', SORD_VERSION, top, out) | |||||
| # Man pages | |||||
| bld.install_files('${MANDIR}/man1', bld.path.ant_glob('doc/*.1')) | |||||
| bld.add_post_fun(autowaf.run_ldconfig) | |||||
| if bld.env.DOCS: | |||||
| bld.add_post_fun(fix_docs) | |||||
| def lint(ctx): | |||||
| subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/*.* sord/* src/zix/*.*', shell=True) | |||||
| def fix_docs(ctx): | |||||
| if ctx.cmd == 'build': | |||||
| autowaf.make_simple_dox(APPNAME) | |||||
| def upload_docs(ctx): | |||||
| os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/sord/') | |||||
| for page in glob.glob('doc/*.[1-8]'): | |||||
| os.system('soelim %s | pre-grohtml troff -man -wall -Thtml | post-grohtml > build/%s.html' % (page, page)) | |||||
| os.system('rsync -avz --delete -e ssh build/%s.html drobilla@drobilla.net:~/drobilla.net/man/' % page) | |||||
| def test(ctx): | |||||
| blddir = autowaf.build_dir(APPNAME, 'tests') | |||||
| try: | |||||
| os.makedirs(blddir) | |||||
| except: | |||||
| pass | |||||
| for i in glob.glob(blddir + '/*.*'): | |||||
| os.remove(i) | |||||
| srcdir = ctx.path.abspath() | |||||
| orig_dir = os.path.abspath(os.curdir) | |||||
| os.chdir(srcdir) | |||||
| good_tests = glob.glob('tests/test-*.ttl') | |||||
| good_tests.sort() | |||||
| os.chdir(orig_dir) | |||||
| autowaf.pre_test(ctx, APPNAME) | |||||
| os.environ['PATH'] = '.' + os.pathsep + os.getenv('PATH') | |||||
| nul = os.devnull | |||||
| snippet = '<s> <p> <o> .' | |||||
| if sys.platform == "win32": | |||||
| snippet = snippet.replace('<', '^<').replace('>', '^>') | |||||
| else: | |||||
| snippet = '"%s"' % snippet | |||||
| autowaf.run_tests(ctx, APPNAME, [ | |||||
| 'sordi_static "file://%s/tests/manifest.ttl" > %s' % (srcdir, nul), | |||||
| 'sordi_static "%s/tests/UTF-8.ttl" > %s' % (srcdir, nul), | |||||
| 'sordi_static -v > %s' % nul, | |||||
| 'sordi_static -h > %s' % nul, | |||||
| 'sordi_static -s "<foo> a <#Thingie> ." file:///test > %s' % nul, | |||||
| 'echo %s | sordi_static - http://example.org/' % snippet, | |||||
| 'echo %s | sordi_static -o turtle - http://example.org/' % snippet, | |||||
| 'sordi_static %s > %s' % (nul, nul)], | |||||
| 0, name='sordi-cmd-good') | |||||
| autowaf.run_tests(ctx, APPNAME, [ | |||||
| 'sordi_static > %s' % nul, | |||||
| 'sordi_static ftp://example.org/unsupported.ttl > %s' % nul, | |||||
| 'sordi_static -i > %s' % nul, | |||||
| 'sordi_static -o > %s' % nul, | |||||
| 'sordi_static -z > %s' % nul, | |||||
| 'sordi_static -p > %s' % nul, | |||||
| 'sordi_static -c > %s' % nul, | |||||
| 'sordi_static -i illegal > %s' % nul, | |||||
| 'sordi_static -o illegal > %s' % nul, | |||||
| 'sordi_static -i turtle > %s' % nul, | |||||
| 'sordi_static -i ntriples > %s' % nul, | |||||
| 'echo "<s> <p> <o> ." | sordi_static -', | |||||
| 'sordi_static /no/such/file > %s' % nul], | |||||
| 1, name='sordi-cmd-bad') | |||||
| autowaf.run_tests(ctx, APPNAME, ['sord_test']) | |||||
| commands = [] | |||||
| for test in good_tests: | |||||
| base_uri = 'http://www.w3.org/2001/sw/DataAccess/df1/' + test.replace('\\', '/') | |||||
| commands += [ 'sordi_static "%s" "%s" > %s.out' % ( | |||||
| os.path.join(srcdir, test), base_uri, test) ] | |||||
| autowaf.run_tests(ctx, APPNAME, commands, 0, name='good') | |||||
| Logs.pprint('BOLD', '\nVerifying turtle => ntriples') | |||||
| for test in good_tests: | |||||
| out_filename = test + '.out' | |||||
| cmp_filename = srcdir + '/' + test.replace('.ttl', '.out') | |||||
| if not os.access(out_filename, os.F_OK): | |||||
| Logs.pprint('RED', 'FAIL: %s output is missing' % test) | |||||
| else: | |||||
| out_lines = sorted(open(out_filename).readlines()) | |||||
| cmp_lines = sorted(open(cmp_filename).readlines()) | |||||
| if out_lines != cmp_lines: | |||||
| Logs.pprint('RED', 'FAIL: %s is incorrect' % out_filename) | |||||
| else: | |||||
| Logs.pprint('GREEN', 'Pass: %s' % test) | |||||
| autowaf.post_test(ctx, APPNAME) | |||||