Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

入门:C

libpurrdf 是基于原生 PurRDF 栈、稳定且遵循 SemVer 纪律的 extern "C" 接口:解析、序列化、模式迭代、写时复制修改、SPARQL、SHACL 验证/蕴涵,以及 GTS 容器往返。已提交、可复现的头文件 include/purrdf.h 就是 ABI 契约——若它与 crate 发生漂移,CI 即失败。

它是一个单一的共享库:libpurrdf 以静态方式复用 purrdf-gts Rust crate,因此语言垫片只需链接 libpurrdf,就仍然可以读写 .gts 容器。

构建

库、头文件与 pkg-config 文件由 cargo-c 生成:

make capi-build                 # cargo capi build:生成 libpurrdf.{so,a} + purrdf.h + purrdf.pc
make capi-install PREFIX=/usr   # cargo capi install:安装到某个前缀目录
make capi-check                 # 验证已提交的头文件是最新的,并运行 C 冒烟测试

第一个程序

改编自仓库中的 C 冒烟测试(crates/rdf-capi/tests/smoke.c):

#include "purrdf.h"
#include <stdio.h>
#include <string.h>

int main(void) {
    const char *doc = "<http://a> <http://b> <http://c> .";
    PurrdfDataset *dataset = NULL;
    PurrdfError *error = NULL;

    int rc = purrdf_parse((const uint8_t *)doc, strlen(doc), "text/turtle",
                          NULL, NULL, &dataset, &error);
    if (rc != PURRDF_STATUS_OK) return 1;

    size_t quad_count = 0;
    purrdf_dataset_quad_count(dataset, &quad_count);
    printf("%zu quad(s)\n", quad_count);

    /* 通过模式游标迭代每一个四元组。 */
    PurrdfGraphMatch any;
    memset(&any, 0, sizeof(any));
    any.kind = PURRDF_GRAPH_MATCH_KIND_ANY;
    PurrdfCursor *cursor = NULL;
    purrdf_quads_for_pattern(dataset, NULL, NULL, NULL, &any, &cursor, &error);

    PurrdfTermView s, p, o, g;
    uint8_t has_graph = 0;
    while (purrdf_cursor_next(cursor, &s, &p, &o, &g, &has_graph) == PURRDF_STATUS_OK) {
        printf("subject=%.*s\n", (int)s.lexical.len, (const char *)s.lexical.ptr);
    }
    purrdf_cursor_free(cursor);
    purrdf_dataset_free(dataset);
    return 0;
}

purrdf_project 与 purrdf_lift 在同一套句柄模型上增加了确定性的图/表格/Research Object 载体归档。project 返回相互独立的归档缓冲区与损失台账缓冲区;lift 返回一个数据集外加一个台账缓冲区。已编译的 purrdf_project_with_assets 入口接受一个有界的、仅含载荷的 USTAR,并输出一个附带确定性元数据与预览的 RO-Crate。已编译的示例 crates/rdf-capi/examples/projection_roundtrip.c 演示了完整的所有权与释放顺序。profile 与配置见图、表格与 Research Object 投影。

ABI 契约

  • 不跨越边界展开栈。 可失败的入口使用 catch_unwind;捕获到的栈展开式 panic 成为 PURRDF_STATUS_PANIC。没有错误通道的入口在捕获 panic 后返回其保护器的默认值。中止式 panic 和其他进程中止无法转换为状态码。
  • int32_t 状态 + 输出参数。 可能失败的函数返回一个 PurrdfStatus 值,并通过输出指针写出结果。PURRDF_STATUS_CURSOR_EXHAUSTED 是(非错误的)行结束信号。
  • 按 SemVer 冻结的 ABI。 状态枚举只追加;新字段与新函数都是增量式的。purrdf_abi_version 在运行时报告当前的 ABI 版本;PURRDF_ABI_MAJOR/_MINOR/_PATCH 宏给出所编译时所用头文件的同一组三元版本号,比较两者就是检查一个并非自己构建的库的方法。次版本号跟踪导出的签名:对导出符号的参数列表、返回契约或文档所述行为的任何修改都会使它递增,增量式参数也包括在内,因为增量式参数对每个 C 使用方而言仍意味着重新编译。

所有权与生命周期

  • 每个句柄/缓冲区/错误/游标都有恰好一个对应的 *_free(purrdf_dataset_free、purrdf_graph_free、purrdf_cursor_free、purrdf_rowcursor_free、purrdf_buffer_free、purrdf_error_free)。释放 NULL 不做任何操作。
  • C 一侧绝不对 PurrdfStr.ptr 调用 free()——它借用的是库所拥有的内存;若这些字节需要比借用活得更久,请把它们复制出来。来自 purrdf_cursor_next 的项视图,在该游标上的下一次 purrdf_cursor_next 或 purrdf_cursor_free 之前有效。
  • 模式游标会固定住数据集,并从所选的核心索引中惰性拉取行;打开游标不会为匹配行分配快照。
  • PurrdfDataset 是冻结的,且为 Send + Sync——可以从多个线程并发读取。PurrdfGraph(写时复制的可变增量)与游标是单线程的。

GTS star 层往返

GTS star 层往返(对包含引用三元组 / 具体化节点(reifier)绑定的数据集执行 purrdf_to_gts → purrdf_from_gts)以 PURRDF_STATUS_OK 成功,与不含 star 内容的往返相同。C ABI 调用的是内核的标准路径(to_gts → read_graph → import_gts_graph);一个特征化测试,即 crates/rdf-capi/tests/abi.rs 中的 gts_star_roundtrip_preserves_the_statement_layer,固定了恢复后数据集中的引用三元组与具体化节点绑定,因此任一层出现回归都会在那里失败。

完整的契约细节——状态码、项跨越边界时的表示、每种句柄的线程安全性——见 purrdf-capi README。