コード例 #1
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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;
}
コード例 #2
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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;
}
コード例 #3
0
SordNode*
sord_get(SordModel*      model,
         const SordNode* s,
         const SordNode* p,
         const SordNode* o,
         const SordNode* g)
{
	if ((bool)s + (bool)p + (bool)o != 2) {
		return NULL;
	}

	SordIter* i   = sord_search(model, s, p, o, g);
	SordNode* ret = NULL;
	if (!s) {
		ret = sord_node_copy(sord_iter_get_node(i, SORD_SUBJECT));
	} else if (!p) {
		ret = sord_node_copy(sord_iter_get_node(i, SORD_PREDICATE));
	} else if (!o) {
		ret = sord_node_copy(sord_iter_get_node(i, SORD_OBJECT));
	}

	sord_iter_free(i);
	return ret;
}
コード例 #4
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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;
}
コード例 #5
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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;
}
コード例 #6
0
ファイル: query.c プロジェクト: AndreeeCZ/muse
LilvNodes*
lilv_nodes_from_stream_objects(LilvWorld*    world,
                               SordIter*     stream,
                               SordQuadIndex field)
{
	if (sord_iter_end(stream)) {
		sord_iter_free(stream);
		return NULL;
	} else if (world->opt.filter_language) {
		return lilv_nodes_from_stream_objects_i18n(world, stream, field);
	} else {
		LilvNodes* values = lilv_nodes_new();
		FOREACH_MATCH(stream) {
			const SordNode* value = sord_iter_get_node(stream, field);
			LilvNode*       node  = lilv_node_new_from_node(world, value);
			if (node) {
				zix_tree_insert((ZixTree*)values, node, NULL);
			}
		}
		sord_iter_free(stream);
		return values;
	}
}
コード例 #7
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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;
}
コード例 #8
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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;
}
コード例 #9
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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
}
コード例 #10
0
ファイル: sord_validate.c プロジェクト: ViktorNova/Carla
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
}
コード例 #11
0
ファイル: query.c プロジェクト: AndreeeCZ/muse
static LilvNodes*
lilv_nodes_from_stream_objects_i18n(LilvWorld*    world,
                                    SordIter*     stream,
                                    SordQuadIndex field)
{
	LilvNodes*      values  = lilv_nodes_new();
	const SordNode* nolang  = NULL;  // Untranslated value
	const SordNode* partial = NULL;  // Partial language match
	char*           syslang = lilv_get_lang();
	FOREACH_MATCH(stream) {
		const SordNode* value = sord_iter_get_node(stream, field);
		if (sord_node_get_type(value) == SORD_LITERAL) {
			const char*   lang = sord_node_get_language(value);
			LilvLangMatch lm   = LILV_LANG_MATCH_NONE;
			if (lang) {
				lm = (syslang)
					? lilv_lang_matches(lang, syslang)
					: LILV_LANG_MATCH_PARTIAL;
			} else {
				nolang = value;
				if (!syslang) {
					lm = LILV_LANG_MATCH_EXACT;
				}
			}

			if (lm == LILV_LANG_MATCH_EXACT) {
				// Exact language match, add to results
				zix_tree_insert((ZixTree*)values,
				                lilv_node_new_from_node(world, value),
				                NULL);
			} else if (lm == LILV_LANG_MATCH_PARTIAL) {
				// Partial language match, save in case we find no exact
				partial = value;
			}
		} else {
			zix_tree_insert((ZixTree*)values,
			                lilv_node_new_from_node(world, value),
			                NULL);
		}
	}
	sord_iter_free(stream);
	free(syslang);

	if (lilv_nodes_size(values) > 0) {
		return values;
	}

	const SordNode* best = nolang;
	if (syslang && partial) {
		// Partial language match for system language
		best = partial;
	} else if (!best) {
		// No languages matches at all, and no untranslated value
		// Use any value, if possible
		best = partial;
	}

	if (best) {
		zix_tree_insert(
			(ZixTree*)values, lilv_node_new_from_node(world, best), NULL);
	} else {
		// No matches whatsoever
		lilv_nodes_free(values);
		values = NULL;
	}

	return values;
}
コード例 #12
0
ファイル: state.c プロジェクト: dmlloyd/Carla
static LilvState*
new_state_from_model(LilvWorld*       world,
                     LV2_URID_Map*    map,
                     SordModel*       model,
                     const SordNode*  node,
                     const char*      dir)
{
	LilvState* const state = (LilvState*)malloc(sizeof(LilvState));
	memset(state, '\0', sizeof(LilvState));
	state->dir       = lilv_strdup(dir);
	state->atom_Path = map->map(map->handle, LV2_ATOM__Path);

	// Get the plugin URI this state applies to
	SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0);
	if (i) {
		const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
		const SordNode* graph  = sord_iter_get_node(i, SORD_GRAPH);
		state->plugin_uri = lilv_node_new_from_node(world, object);
		if (!state->dir && graph) {
			state->dir = lilv_strdup((const char*)sord_node_get_string(graph));
		}
		sord_iter_free(i);
	} else if (sord_ask(model,
	                    node,
	                    world->uris.rdf_a,
	                    world->uris.lv2_Plugin, 0)) {
		// Loading plugin description as state (default state)
		state->plugin_uri = lilv_node_new_from_node(world, node);
	} else {
		LILV_ERRORF("State %s missing lv2:appliesTo property\n",
		            sord_node_get_string(node));
	}

	// Get the state label
	i = sord_search(model, node, world->uris.rdfs_label, NULL, NULL);
	if (i) {
		const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
		const SordNode* graph  = sord_iter_get_node(i, SORD_GRAPH);
		state->label = lilv_strdup((const char*)sord_node_get_string(object));
		if (!state->dir) {
			state->dir = lilv_strdup((const char*)sord_node_get_string(graph));
		}
		sord_iter_free(i);
	}

	Sratom*        sratom = sratom_new(map);
	SerdChunk      chunk  = { NULL, 0 };
	LV2_Atom_Forge forge;
	lv2_atom_forge_init(&forge, map);
	lv2_atom_forge_set_sink(
		&forge, sratom_forge_sink, sratom_forge_deref, &chunk);

	// Get port values
	SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0);
	FOREACH_MATCH(ports) {
		const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);

		SordNode* label  = sord_get(model, port, world->uris.rdfs_label, 0, 0);
		SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0);
		SordNode* value  = sord_get(model, port, world->uris.pset_value, 0, 0);
		if (!value) {
			value = sord_get(model, port, world->uris.lv2_default, 0, 0);
		}
		if (!symbol) {
			LILV_ERRORF("State `%s' port missing symbol.\n",
			            sord_node_get_string(node));
		} else if (value) {
			chunk.len = 0;
			sratom_read(sratom, &forge, world->world, model, value);
			LV2_Atom* atom = (LV2_Atom*)chunk.buf;

			append_port_value(state,
			                  (const char*)sord_node_get_string(symbol),
			                  LV2_ATOM_BODY(atom), atom->size, atom->type);

			if (label) {
				lilv_state_set_label(state,
				                     (const char*)sord_node_get_string(label));
			}
		}
		sord_node_free(world->world, value);
		sord_node_free(world->world, symbol);
		sord_node_free(world->world, label);
	}
	sord_iter_free(ports);

	// Get properties
	SordNode* statep     = sord_new_uri(world->world, USTR(LV2_STATE__state));
	SordNode* state_node = sord_get(model, node, statep, NULL, NULL);
	if (state_node) {
		SordIter* props = sord_search(model, state_node, 0, 0, 0);
		FOREACH_MATCH(props) {
			const SordNode* p = sord_iter_get_node(props, SORD_PREDICATE);
			const SordNode* o = sord_iter_get_node(props, SORD_OBJECT);

			chunk.len = 0;
			lv2_atom_forge_set_sink(
				&forge, sratom_forge_sink, sratom_forge_deref, &chunk);

			sratom_read(sratom, &forge, world->world, model, o);
			LV2_Atom* atom  = (LV2_Atom*)chunk.buf;
			uint32_t  flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
			Property  prop  = { NULL, 0, 0, 0, flags };

			prop.key   = map->map(map->handle, (const char*)sord_node_get_string(p));
			prop.type  = atom->type;
			prop.size  = atom->size;
			prop.value = malloc(atom->size);
			memcpy(prop.value, LV2_ATOM_BODY(atom), atom->size);
			if (atom->type == forge.Path) {
				prop.flags = LV2_STATE_IS_PORTABLE;
			}

			if (prop.value) {
				state->props = (Property*)realloc(
					state->props, (++state->num_props) * sizeof(Property));
				state->props[state->num_props - 1] = prop;
			}
		}
		sord_iter_free(props);
	}
	sord_node_free(world->world, state_node);
	sord_node_free(world->world, statep);

	free((void*)chunk.buf);
	sratom_free(sratom);

	qsort(state->props, state->num_props, sizeof(Property), property_cmp);
	qsort(state->values, state->num_values, sizeof(PortValue), value_cmp);

	return state;
}