Branch :
/* c3
* Copyright 2022 kmx.io <contact@kmx.io>
*
* Permission is hereby granted to use this software granted
* the above copyright notice and this permission paragraph
* are included in all copies and substantial portions of this
* software.
*
* THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
* PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
* AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
* THIS SOFTWARE.
*/
#include <assert.h>
#include <err.h>
#include <stdlib.h>
#include "buf.h"
#include "buf_file.h"
#include "buf_inspect.h"
#include "buf_parse.h"
#include "fact.h"
#include "facts.h"
#include "facts_cursor.h"
#include "facts_with.h"
#include "set__fact.h"
#include "set__tag.h"
#include "skiplist__fact.h"
#include "tag.h"
s_fact * facts_add_fact (s_facts *facts, const s_fact *fact)
{
s_fact tmp;
s_fact *f = NULL;
s_set_item__fact *item;
assert(facts);
assert(fact);
tmp.subject = facts_ref_tag(facts, fact->subject);
tmp.predicate = facts_ref_tag(facts, fact->predicate);
tmp.object = facts_ref_tag(facts, fact->object);
if ((item = set_get__fact(&facts->facts, &tmp)))
return &item->data;
if (facts->log)
facts_log_add(facts->log, &tmp);
item = set_add__fact(&facts->facts, &tmp);
f = &item->data;
skiplist_insert__fact(facts->index_spo, f);
skiplist_insert__fact(facts->index_pos, f);
skiplist_insert__fact(facts->index_osp, f);
return f;
}
s_fact * facts_add_tags (s_facts *facts, const s_tag *subject,
const s_tag *predicate,
const s_tag *object)
{
s_fact fact;
fact_init(&fact, subject, predicate, object);
return facts_add_fact(facts, &fact);
}
void facts_clean (s_facts *facts)
{
if (facts->log)
buf_file_close(facts->log);
skiplist_delete__fact(facts->index_osp);
skiplist_delete__fact(facts->index_pos);
skiplist_delete__fact(facts->index_spo);
set_clean__fact(&facts->facts);
set_clean__tag(&facts->tags);
}
void facts_delete (s_facts *facts)
{
assert(facts);
facts_clean(facts);
free(facts);
}
sw facts_dump (const s_facts *facts, const s8 *path)
{
s_buf buf;
s_facts_cursor cursor;
s_fact *fact;
FILE *fp;
s_tag predicate;
s_tag object;
sw r;
sw result = 0;
s_tag subject;
assert(facts);
assert(path);
BUF_INIT_ALLOCA(&buf, 1024);
fp = fopen(path, "w");
buf_file_open_w(&buf, fp);
tag_init_var(&subject);
tag_init_var(&predicate);
tag_init_var(&object);
facts_with_0(facts, &cursor, &subject, &predicate, &object);
while ((fact = facts_cursor_next(&cursor))) {
if ((r = facts_log_add(&buf, fact)) < 0)
goto clean;
result += r;
}
clean:
buf_file_close(&buf);
fclose(fp);
return result;
}
s_fact * facts_find_fact (const s_facts *facts, const s_fact *fact)
{
s_fact f;
s_set_item__fact *item;
assert(facts);
assert(fact);
if ((f.subject = facts_find_tag(facts, fact->subject)) &&
(f.predicate = facts_find_tag(facts, fact->predicate)) &&
(f.object = facts_find_tag(facts, fact->object)) &&
(item = set_get__fact((const s_set__fact *) &facts->facts, &f)))
return &item->data;
return NULL;
}
s_tag * facts_find_tag (const s_facts *facts, const s_tag *tag)
{
s_set_item__tag *item;
assert(facts);
assert(tag);
if ((item = set_get__tag(&facts->tags, tag)))
return &item->data;
return NULL;
}
s_facts * facts_init (s_facts *facts, s_buf *log)
{
const u8 max_height = 10;
const double spacing = 2.7;
assert(facts);
set_init__tag(&facts->tags, 1024);
set_init__fact(&facts->facts, 1024);
facts->index_spo = skiplist_new__fact(max_height, spacing);
assert(facts->index_spo);
facts->index_spo->compare = fact_compare;
facts->index_pos = skiplist_new__fact(max_height, spacing);
assert(facts->index_pos);
facts->index_pos->compare = fact_compare_pos;
facts->index_osp = skiplist_new__fact(max_height, spacing);
assert(facts->index_osp);
facts->index_osp->compare = fact_compare_osp;
facts->log = log;
return facts;
}
/* XXX nested databases should be used with care */
sw facts_load (s_facts *facts, const s8 *path)
{
s_buf buf;
s_fact fact;
FILE *fp;
sw r;
sw result = 0;
assert(facts);
assert(path);
BUF_INIT_ALLOCA(&buf, 1024);
fp = fopen(path, "r");
buf_file_open_r(&buf, fp);
while (1) {
if ((r = buf_read_1(&buf, "add ")) < 0)
break;
result += r;
if (r) {
if ((r = buf_parse_fact(&buf, &fact)) <= 0)
break;
result += r;
facts_add_fact(facts, &fact);
goto ok;
}
if ((r = buf_read_1(&buf, "remove ")) <= 0)
break;
result += r;
if ((r = buf_parse_fact(&buf, &fact)) <= 0)
break;
result += r;
facts_remove_fact(facts, &fact);
ok:
buf_read_1(&buf, "\n");
}
buf_file_close(&buf);
fclose(fp);
return result;
}
sw facts_log_add (s_buf *log, const s_fact *fact)
{
sw r;
sw result = 0;
if ((r = buf_write_1(log, "add ")) < 0)
return r;
result += r;
if ((r = buf_inspect_fact(log, fact)) < 0)
return r;
result += r;
if ((r = buf_write_1(log, "\n")) < 0)
return r;
result += r;
return result;
}
sw facts_log_remove (s_buf *log, const s_fact *fact)
{
sw r;
sw result = 0;
if ((r = buf_write_1(log, "remove ")) < 0)
return r;
result += r;
if ((r = buf_inspect_fact(log, fact)) < 0)
return r;
result += r;
if ((r = buf_write_1(log, "\n")) < 0)
return r;
result += r;
return result;
}
s_facts * facts_new (s_buf *log)
{
s_facts *n;
if (! (n = malloc(sizeof(s_facts))))
errx(1, "facts_new: out of memory");
return facts_init(n, log);
}
s_tag * facts_ref_tag (s_facts *facts, const s_tag *tag)
{
s_set_item__tag *item;
assert(facts);
assert(tag);
item = set_add__tag(&facts->tags, tag);
item->usage++;
return &item->data;
}
e_bool facts_remove_fact (s_facts *facts, const s_fact *fact)
{
s_fact f;
s_fact *found;
assert(facts);
assert(fact);
found = facts_find_fact(facts, fact);
if (found) {
if (facts->log)
facts_log_remove(facts->log, found);
skiplist_remove__fact(facts->index_spo, found);
skiplist_remove__fact(facts->index_pos, found);
skiplist_remove__fact(facts->index_osp, found);
f = *found;
set_remove__fact(&facts->facts, found);
facts_unref_tag(facts, f.subject);
facts_unref_tag(facts, f.predicate);
facts_unref_tag(facts, f.object);
return true;
}
return false;
}
e_bool facts_unref_tag (s_facts *facts, const s_tag *tag)
{
s_set_item__tag *item;
assert(facts);
assert(tag);
item = set_get__tag(&facts->tags, tag);
if (item) {
item->usage--;
if (! item->usage)
set_remove_item__tag(&facts->tags, item);
return true;
}
return false;
}