commit 1fb88a912a595552475593261df7e3c7c6b725c3
parent 954f48b23df64ec395a394f4118ebd800a65c3fe
Author: William Casarin <jb55@jb55.com>
Date: Thu, 25 Jan 2024 14:33:44 -0800
nostrdb: port everything over to be in as sync as possible
for now
Diffstat:
36 files changed, 4287 insertions(+), 426 deletions(-)
diff --git a/nostrdb/Makefile b/nostrdb/Makefile
@@ -0,0 +1,168 @@
+CFLAGS = -Wall -Wno-misleading-indentation -Wno-unused-function -Werror -O2 -g -Ideps/secp256k1/include -Ideps/lmdb -Ideps/flatcc/include
+HEADERS = sha256.h nostrdb.h cursor.h hex.h jsmn.h config.h sha256.h random.h memchr.h cpu.h $(C_BINDINGS)
+FLATCC_SRCS=deps/flatcc/src/runtime/json_parser.c deps/flatcc/src/runtime/verifier.c deps/flatcc/src/runtime/builder.c deps/flatcc/src/runtime/emitter.c deps/flatcc/src/runtime/refmap.c
+BOLT11_SRCS = bolt11/bolt11.c bolt11/bech32.c bolt11/tal.c bolt11/talstr.c bolt11/take.c bolt11/list.c bolt11/utf8.c bolt11/amount.c bolt11/hash_u5.c
+SRCS = nostrdb.c sha256.c $(BOLT11_SRCS) $(FLATCC_SRCS)
+LDS = $(OBJS) $(ARS)
+OBJS = $(SRCS:.c=.o)
+DEPS = $(OBJS) $(HEADERS) $(ARS)
+ARS = deps/lmdb/liblmdb.a deps/secp256k1/.libs/libsecp256k1.a
+LMDB_VER=0.9.31
+FLATCC_VER=05dc16dc2b0316e61063bb1fc75426647badce48
+PREFIX ?= /usr/local
+SUBMODULES = deps/secp256k1
+C_BINDINGS_PROFILE=bindings/c/profile_builder.h bindings/c/profile_reader.h bindings/c/profile_verifier.h bindings/c/profile_json_parser.h
+C_BINDINGS_META=bindings/c/meta_builder.h bindings/c/meta_reader.h bindings/c/meta_verifier.h bindings/c/meta_json_parser.h
+C_BINDINGS_COMMON=bindings/c/flatbuffers_common_builder.h bindings/c/flatbuffers_common_reader.h
+C_BINDINGS=$(C_BINDINGS_COMMON) $(C_BINDINGS_PROFILE) $(C_BINDINGS_META)
+BINDINGS=bindings
+BIN=ndb
+
+CHECKDATA=testdata/db/v0/data.mdb
+
+all: lib ndb
+
+lib: benches test
+
+ndb: ndb.c $(DEPS)
+ $(CC) $(CFLAGS) ndb.c $(LDS) -o $@
+
+bindings: bindings-swift bindings-rust bindings-c
+
+check: test $(CHECKDATA)
+ rm -rf testdata/db/*.mdb
+ ./test || rm -rf testdata/db/v0
+ rm -rf testdata/db/v0
+
+clean:
+ rm -rf test bench bench-ingest bench-ingest-many
+
+benches: bench
+
+distclean: clean
+ rm -rf deps
+
+tags:
+ find . -name '*.c' -or -name '*.h' | xargs ctags
+
+configurator: configurator.c
+ $(CC) $< -o $@
+
+config.h: configurator
+ ./configurator > $@
+
+bindings-c: $(C_BINDINGS)
+
+bindings/%/.dir:
+ mkdir -p $(shell dirname $@)
+ touch $@
+
+bindings/c/%_builder.h: schemas/%.fbs bindings/c/.dir
+ flatcc --builder $< -o bindings/c
+
+bindings/c/%_verifier.h bindings/c/%_reader.h: schemas/%.fbs bindings/c/.dir
+ flatcc --verifier -o bindings/c $<
+
+bindings/c/flatbuffers_common_reader.h: bindings/c/.dir
+ flatcc --common_reader -o bindings/c
+
+bindings/c/flatbuffers_common_builder.h: bindings/c/.dir
+ flatcc --common_builder -o bindings/c
+
+bindings/c/%_json_parser.h: schemas/%.fbs bindings/c/.dir
+ flatcc --json-parser $< -o bindings/c
+
+bindings-rust: bindings/rust/ndb_profile.rs bindings/rust/ndb_meta.rs
+
+bindings/rust/ndb_profile.rs: schemas/profile.fbs bindings/rust
+ flatc --gen-json-emit --rust $<
+ @mv profile_generated.rs $@
+
+bindings/rust/ndb_meta.rs: schemas/meta.fbs bindings/swift
+ flatc --rust $<
+ @mv meta_generated.rs $@
+
+bindings-swift: bindings/swift/NdbProfile.swift bindings/swift/NdbMeta.swift
+
+bindings/swift/NdbProfile.swift: schemas/profile.fbs bindings/swift
+ flatc --gen-json-emit --swift $<
+ @mv profile_generated.swift $@
+
+bindings/swift/NdbMeta.swift: schemas/meta.fbs bindings/swift
+ flatc --swift $<
+ @mv meta_generated.swift $@
+
+deps/.dir:
+ @mkdir -p deps
+ touch deps/.dir
+
+deps/LMDB_$(LMDB_VER).tar.gz: deps/.dir
+ curl -L https://github.com/LMDB/lmdb/archive/refs/tags/LMDB_$(LMDB_VER).tar.gz -o $@
+
+deps/flatcc_$(FLATCC_VER).tar.gz: deps/.dir
+ curl -L https://github.com/jb55/flatcc/archive/$(FLATCC_VER).tar.gz -o $@
+
+deps/flatcc/src/runtime/json_parser.c: deps/flatcc_$(FLATCC_VER).tar.gz deps/.dir
+ tar xf $<
+ rm -rf deps/flatcc
+ mv flatcc-$(FLATCC_VER) deps/flatcc
+ touch $@
+
+deps/lmdb/lmdb.h: deps/LMDB_$(LMDB_VER).tar.gz deps/.dir
+ tar xf $<
+ rm -rf deps/lmdb
+ mv lmdb-LMDB_$(LMDB_VER)/libraries/liblmdb deps/lmdb
+ rm -rf lmdb-LMDB_$(LMDB_VER)
+ touch $@
+
+deps/secp256k1/.git: deps/.dir
+ @devtools/refresh-submodules.sh $(SUBMODULES)
+
+deps/secp256k1/include/secp256k1.h: deps/secp256k1/.git
+
+deps/secp256k1/configure: deps/secp256k1/.git
+ cd deps/secp256k1; \
+ ./autogen.sh
+
+deps/secp256k1/.libs/libsecp256k1.a: deps/secp256k1/config.log
+ cd deps/secp256k1; \
+ make -j libsecp256k1.la
+
+deps/secp256k1/config.log: deps/secp256k1/configure
+ cd deps/secp256k1; \
+ ./configure --disable-shared --enable-module-ecdh --enable-module-schnorrsig --enable-module-extrakeys
+
+deps/lmdb/liblmdb.a: deps/lmdb/lmdb.h
+ $(MAKE) -C deps/lmdb liblmdb.a
+
+bench: bench.c $(DEPS)
+ $(CC) $(CFLAGS) bench.c $(LDS) -o $@
+
+testdata/db/ndb-v0.tar.zst:
+ curl https://cdn.jb55.com/s/ndb-v0.tar.zst -o $@
+
+testdata/db/ndb-v0.tar: testdata/db/ndb-v0.tar.zst
+ zstd -d < $< > $@
+
+testdata/db/v0/data.mdb: testdata/db/ndb-v0.tar
+ tar xf $<
+ rm -rf testdata/db/v0
+ mv v0 testdata/db
+
+testdata/many-events.json.zst:
+ curl https://cdn.jb55.com/s/many-events.json.zst -o $@
+
+testdata/many-events.json: testdata/many-events.json.zst
+ zstd -d $<
+
+bench-ingest-many: bench-ingest-many.c $(DEPS) testdata/many-events.json
+ $(CC) $(CFLAGS) $< $(LDS) -o $@
+
+testdata/db/.dir:
+ @mkdir -p testdata/db
+ touch testdata/db/.dir
+
+test: test.c $(DEPS) testdata/db/.dir
+ $(CC) $(CFLAGS) test.c $(LDS) -o $@
+
+.PHONY: tags clean
diff --git a/nostrdb/bench-ingest-many.c b/nostrdb/bench-ingest-many.c
@@ -0,0 +1,75 @@
+
+
+#include "io.h"
+#include "nostrdb.h"
+#include <sys/mman.h>
+#include <time.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+int map_file(const char *filename, unsigned char **p, size_t *flen)
+{
+ struct stat st;
+ int des;
+ stat(filename, &st);
+ *flen = st.st_size;
+
+ des = open(filename, O_RDONLY);
+
+ *p = mmap(NULL, *flen, PROT_READ, MAP_PRIVATE, des, 0);
+ close(des);
+
+ return *p != MAP_FAILED;
+}
+
+static int bench_parser()
+{
+ long nanos, ms;
+ size_t written;
+ struct ndb *ndb;
+ struct timespec t1, t2;
+ char *json;
+ int times = 1;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ ndb_config_set_mapsize(&config, 1024ULL * 1024ULL * 400ULL * 10ULL);
+ ndb_config_set_ingest_threads(&config, 8);
+ ndb_config_set_flags(&config, NDB_FLAG_SKIP_NOTE_VERIFY);
+
+ assert(ndb_init(&ndb, "testdata/db", &config));
+ const char *filename = "testdata/many-events.json";
+ if (!map_file(filename, (unsigned char**)&json, &written)) {
+ printf("mapping testdata/many-events.json failed\n");
+ return 2;
+ }
+
+ printf("mapped %ld bytes in %s\n", written, filename);
+
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+
+ ndb_process_events(ndb, json, written);
+
+ ndb_destroy(ndb);
+
+ clock_gettime(CLOCK_MONOTONIC, &t2);
+
+ nanos = (t2.tv_sec - t1.tv_sec) * (long)1e9 + (t2.tv_nsec - t1.tv_nsec);
+ ms = nanos / 1e6;
+ printf("ns/run\t%ld\nms/run\t%f\nns\t%ld\nms\t%ld\n",
+ nanos/times, (double)ms/(double)times, nanos, ms);
+
+ return 1;
+}
+
+int main(int argc, char *argv[], char **env)
+{
+ if (!bench_parser())
+ return 2;
+
+ return 0;
+}
+
diff --git a/nostrdb/bench.c b/nostrdb/bench.c
@@ -0,0 +1,51 @@
+
+#include "io.h"
+#include "nostrdb.h"
+#include <sys/mman.h>
+#include <time.h>
+#include <stdlib.h>
+
+static int bench_parser(int times, const char *json, int len)
+{
+ static unsigned char buf[2<<18];
+
+ struct timespec t1, t2;
+ int i;
+ long nanos, ms;
+ struct ndb_note *note;
+
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+ for (i = 0; i < times; i++) {
+ if (!ndb_note_from_json(json, len, ¬e, buf, sizeof(buf))) {
+ return 0;
+ }
+ }
+ clock_gettime(CLOCK_MONOTONIC, &t2);
+
+ nanos = (t2.tv_sec - t1.tv_sec) * (long)1e9 + (t2.tv_nsec - t1.tv_nsec);
+ ms = nanos / 1e6;
+ printf("ns/run\t%ld\nms/run\t%f\nns\t%ld\nms\t%ld\n",
+ nanos/times, (double)ms/(double)times, nanos, ms);
+
+ return 1;
+}
+
+int main(int argc, char *argv[], char **env)
+{
+ static const int alloc_size = 2 << 18;
+ int times = 10000, len = 0;
+ unsigned char buf[alloc_size];
+
+ if (!read_file("testdata/contacts.json", buf, alloc_size, &len))
+ return 1;
+
+ if (argc >= 2)
+ times = atoi(argv[1]);
+
+ fprintf(stderr, "benching parser %d times\n", times);
+ if (!bench_parser(times, (const char*)&buf[0], len))
+ return 2;
+
+ return 0;
+}
+
diff --git a/nostrdb/bindings/c/flatbuffers_common_builder.h b/nostrdb/bindings/c/flatbuffers_common_builder.h
@@ -5,9 +5,9 @@
/* Common FlatBuffers build functionality for C. */
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_prologue.h"
#ifndef FLATBUILDER_H
-#include "flatcc_builder.h"
+#include "flatcc/flatcc_builder.h"
#endif
typedef flatcc_builder_t flatbuffers_builder_t;
typedef flatcc_builder_ref_t flatbuffers_ref_t;
@@ -681,5 +681,5 @@ __flatbuffers_build_scalar(flatbuffers_, flatbuffers_double, double)
__flatbuffers_build_string(flatbuffers_)
__flatbuffers_build_buffer(flatbuffers_)
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* FLATBUFFERS_COMMON_BUILDER_H */
diff --git a/nostrdb/bindings/c/flatbuffers_common_reader.h b/nostrdb/bindings/c/flatbuffers_common_reader.h
@@ -5,8 +5,8 @@
/* Common FlatBuffers read functionality for C. */
-#include "flatcc_prologue.h"
-#include "flatcc_flatbuffers.h"
+#include "flatcc/flatcc_prologue.h"
+#include "flatcc/flatcc_flatbuffers.h"
#define __flatbuffers_read_scalar_at_byteoffset(N, p, o) N ## _read_from_pe((uint8_t *)(p) + (o))
@@ -574,5 +574,5 @@ static inline N ## _ ## K ## t N ## _as_typed_root(const void *buffer__tmp)\
#define __flatbuffers_struct_as_root(N) __flatbuffers_buffer_as_root(N, struct_)
#define __flatbuffers_table_as_root(N) __flatbuffers_buffer_as_root(N, table_)
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* FLATBUFFERS_COMMON_H */
diff --git a/nostrdb/bindings/c/meta_builder.h b/nostrdb/bindings/c/meta_builder.h
@@ -9,7 +9,7 @@
#ifndef FLATBUFFERS_COMMON_BUILDER_H
#include "flatbuffers_common_builder.h"
#endif
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -65,5 +65,5 @@ static NdbEventMeta_ref_t NdbEventMeta_clone(flatbuffers_builder_t *B, NdbEventM
__flatbuffers_memoize_end(B, t, NdbEventMeta_end(B));
}
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* META_BUILDER_H */
diff --git a/nostrdb/bindings/c/meta_json_parser.h b/nostrdb/bindings/c/meta_json_parser.h
@@ -3,8 +3,8 @@
/* Generated by flatcc 0.6.1 FlatBuffers schema compiler for C by dvide.com */
-#include "flatcc_json_parser.h"
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_json_parser.h"
+#include "flatcc/flatcc_prologue.h"
/*
* Parses the default root table or struct of the schema and constructs a FlatBuffer.
@@ -246,5 +246,5 @@ static int meta_parse_json(flatcc_builder_t *B, flatcc_json_parser_t *ctx,
return 0;
}
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* META_JSON_PARSER_H */
diff --git a/nostrdb/bindings/c/meta_reader.h b/nostrdb/bindings/c/meta_reader.h
@@ -6,11 +6,11 @@
#ifndef FLATBUFFERS_COMMON_READER_H
#include "flatbuffers_common_reader.h"
#endif
-#include "flatcc_flatbuffers.h"
+#include "flatcc/flatcc_flatbuffers.h"
#ifndef __alignas_is_defined
#include <stdalign.h>
#endif
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -54,5 +54,5 @@ __flatbuffers_define_scalar_field(4, NdbEventMeta, zaps, flatbuffers_int32, int3
__flatbuffers_define_scalar_field(5, NdbEventMeta, zap_total, flatbuffers_int64, int64_t, INT64_C(0))
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* META_READER_H */
diff --git a/nostrdb/bindings/c/meta_verifier.h b/nostrdb/bindings/c/meta_verifier.h
@@ -6,8 +6,8 @@
#ifndef META_READER_H
#include "meta_reader.h"
#endif
-#include "flatcc_verifier.h"
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_verifier.h"
+#include "flatcc/flatcc_prologue.h"
static int NdbEventMeta_verify_table(flatcc_table_verifier_descriptor_t *td);
@@ -43,5 +43,5 @@ static inline int NdbEventMeta_verify_as_root_with_type_hash(const void *buf, si
return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &NdbEventMeta_verify_table);
}
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* META_VERIFIER_H */
diff --git a/nostrdb/bindings/c/profile_builder.h b/nostrdb/bindings/c/profile_builder.h
@@ -9,7 +9,7 @@
#ifndef FLATBUFFERS_COMMON_BUILDER_H
#include "flatbuffers_common_builder.h"
#endif
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -127,5 +127,5 @@ static NdbProfileRecord_ref_t NdbProfileRecord_clone(flatbuffers_builder_t *B, N
__flatbuffers_memoize_end(B, t, NdbProfileRecord_end(B));
}
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* PROFILE_BUILDER_H */
diff --git a/nostrdb/bindings/c/profile_json_parser.h b/nostrdb/bindings/c/profile_json_parser.h
@@ -3,8 +3,8 @@
/* Generated by flatcc 0.6.1 FlatBuffers schema compiler for C by dvide.com */
-#include "flatcc_json_parser.h"
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_json_parser.h"
+#include "flatcc/flatcc_prologue.h"
/*
* Parses the default root table or struct of the schema and constructs a FlatBuffer.
@@ -408,5 +408,5 @@ static int profile_parse_json(flatcc_builder_t *B, flatcc_json_parser_t *ctx,
return 0;
}
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* PROFILE_JSON_PARSER_H */
diff --git a/nostrdb/bindings/c/profile_reader.h b/nostrdb/bindings/c/profile_reader.h
@@ -6,11 +6,11 @@
#ifndef FLATBUFFERS_COMMON_READER_H
#include "flatbuffers_common_reader.h"
#endif
-#include "flatcc_flatbuffers.h"
+#include "flatcc/flatcc_flatbuffers.h"
#ifndef __alignas_is_defined
#include <stdalign.h>
#endif
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -89,5 +89,5 @@ __flatbuffers_define_scalar_field(2, NdbProfileRecord, note_key, flatbuffers_uin
__flatbuffers_define_string_field(3, NdbProfileRecord, lnurl, 0)
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* PROFILE_READER_H */
diff --git a/nostrdb/bindings/c/profile_verifier.h b/nostrdb/bindings/c/profile_verifier.h
@@ -6,8 +6,8 @@
#ifndef PROFILE_READER_H
#include "profile_reader.h"
#endif
-#include "flatcc_verifier.h"
-#include "flatcc_prologue.h"
+#include "flatcc/flatcc_verifier.h"
+#include "flatcc/flatcc_prologue.h"
static int NdbProfile_verify_table(flatcc_table_verifier_descriptor_t *td);
static int NdbProfileRecord_verify_table(flatcc_table_verifier_descriptor_t *td);
@@ -80,5 +80,5 @@ static inline int NdbProfileRecord_verify_as_root_with_type_hash(const void *buf
return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &NdbProfileRecord_verify_table);
}
-#include "flatcc_epilogue.h"
+#include "flatcc/flatcc_epilogue.h"
#endif /* PROFILE_VERIFIER_H */
diff --git a/nostrdb/bindings/rust/.dir b/nostrdb/bindings/rust/.dir
diff --git a/nostrdb/bindings/rust/ndb_meta.rs b/nostrdb/bindings/rust/ndb_meta.rs
@@ -0,0 +1,264 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+// @generated
+
+use core::mem;
+use core::cmp::Ordering;
+
+extern crate flatbuffers;
+use self::flatbuffers::{EndianScalar, Follow};
+
+pub enum NdbEventMetaOffset {}
+#[derive(Copy, Clone, PartialEq)]
+
+pub struct NdbEventMeta<'a> {
+ pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for NdbEventMeta<'a> {
+ type Inner = NdbEventMeta<'a>;
+ #[inline]
+ unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Self { _tab: flatbuffers::Table::new(buf, loc) }
+ }
+}
+
+impl<'a> NdbEventMeta<'a> {
+ pub const VT_RECEIVED_AT: flatbuffers::VOffsetT = 4;
+ pub const VT_REACTIONS: flatbuffers::VOffsetT = 6;
+ pub const VT_QUOTES: flatbuffers::VOffsetT = 8;
+ pub const VT_REPOSTS: flatbuffers::VOffsetT = 10;
+ pub const VT_ZAPS: flatbuffers::VOffsetT = 12;
+ pub const VT_ZAP_TOTAL: flatbuffers::VOffsetT = 14;
+
+ #[inline]
+ pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+ NdbEventMeta { _tab: table }
+ }
+ #[allow(unused_mut)]
+ pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+ _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+ args: &'args NdbEventMetaArgs
+ ) -> flatbuffers::WIPOffset<NdbEventMeta<'bldr>> {
+ let mut builder = NdbEventMetaBuilder::new(_fbb);
+ builder.add_zap_total(args.zap_total);
+ builder.add_zaps(args.zaps);
+ builder.add_reposts(args.reposts);
+ builder.add_quotes(args.quotes);
+ builder.add_reactions(args.reactions);
+ builder.add_received_at(args.received_at);
+ builder.finish()
+ }
+
+
+ #[inline]
+ pub fn received_at(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbEventMeta::VT_RECEIVED_AT, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn reactions(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbEventMeta::VT_REACTIONS, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn quotes(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbEventMeta::VT_QUOTES, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn reposts(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbEventMeta::VT_REPOSTS, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn zaps(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbEventMeta::VT_ZAPS, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn zap_total(&self) -> i64 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i64>(NdbEventMeta::VT_ZAP_TOTAL, Some(0)).unwrap()}
+ }
+}
+
+impl flatbuffers::Verifiable for NdbEventMeta<'_> {
+ #[inline]
+ fn run_verifier(
+ v: &mut flatbuffers::Verifier, pos: usize
+ ) -> Result<(), flatbuffers::InvalidFlatbuffer> {
+ use self::flatbuffers::Verifiable;
+ v.visit_table(pos)?
+ .visit_field::<i32>("received_at", Self::VT_RECEIVED_AT, false)?
+ .visit_field::<i32>("reactions", Self::VT_REACTIONS, false)?
+ .visit_field::<i32>("quotes", Self::VT_QUOTES, false)?
+ .visit_field::<i32>("reposts", Self::VT_REPOSTS, false)?
+ .visit_field::<i32>("zaps", Self::VT_ZAPS, false)?
+ .visit_field::<i64>("zap_total", Self::VT_ZAP_TOTAL, false)?
+ .finish();
+ Ok(())
+ }
+}
+pub struct NdbEventMetaArgs {
+ pub received_at: i32,
+ pub reactions: i32,
+ pub quotes: i32,
+ pub reposts: i32,
+ pub zaps: i32,
+ pub zap_total: i64,
+}
+impl<'a> Default for NdbEventMetaArgs {
+ #[inline]
+ fn default() -> Self {
+ NdbEventMetaArgs {
+ received_at: 0,
+ reactions: 0,
+ quotes: 0,
+ reposts: 0,
+ zaps: 0,
+ zap_total: 0,
+ }
+ }
+}
+
+pub struct NdbEventMetaBuilder<'a: 'b, 'b> {
+ fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
+}
+impl<'a: 'b, 'b> NdbEventMetaBuilder<'a, 'b> {
+ #[inline]
+ pub fn add_received_at(&mut self, received_at: i32) {
+ self.fbb_.push_slot::<i32>(NdbEventMeta::VT_RECEIVED_AT, received_at, 0);
+ }
+ #[inline]
+ pub fn add_reactions(&mut self, reactions: i32) {
+ self.fbb_.push_slot::<i32>(NdbEventMeta::VT_REACTIONS, reactions, 0);
+ }
+ #[inline]
+ pub fn add_quotes(&mut self, quotes: i32) {
+ self.fbb_.push_slot::<i32>(NdbEventMeta::VT_QUOTES, quotes, 0);
+ }
+ #[inline]
+ pub fn add_reposts(&mut self, reposts: i32) {
+ self.fbb_.push_slot::<i32>(NdbEventMeta::VT_REPOSTS, reposts, 0);
+ }
+ #[inline]
+ pub fn add_zaps(&mut self, zaps: i32) {
+ self.fbb_.push_slot::<i32>(NdbEventMeta::VT_ZAPS, zaps, 0);
+ }
+ #[inline]
+ pub fn add_zap_total(&mut self, zap_total: i64) {
+ self.fbb_.push_slot::<i64>(NdbEventMeta::VT_ZAP_TOTAL, zap_total, 0);
+ }
+ #[inline]
+ pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> NdbEventMetaBuilder<'a, 'b> {
+ let start = _fbb.start_table();
+ NdbEventMetaBuilder {
+ fbb_: _fbb,
+ start_: start,
+ }
+ }
+ #[inline]
+ pub fn finish(self) -> flatbuffers::WIPOffset<NdbEventMeta<'a>> {
+ let o = self.fbb_.end_table(self.start_);
+ flatbuffers::WIPOffset::new(o.value())
+ }
+}
+
+impl core::fmt::Debug for NdbEventMeta<'_> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let mut ds = f.debug_struct("NdbEventMeta");
+ ds.field("received_at", &self.received_at());
+ ds.field("reactions", &self.reactions());
+ ds.field("quotes", &self.quotes());
+ ds.field("reposts", &self.reposts());
+ ds.field("zaps", &self.zaps());
+ ds.field("zap_total", &self.zap_total());
+ ds.finish()
+ }
+}
+#[inline]
+/// Verifies that a buffer of bytes contains a `NdbEventMeta`
+/// and returns it.
+/// Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `root_as_ndb_event_meta_unchecked`.
+pub fn root_as_ndb_event_meta(buf: &[u8]) -> Result<NdbEventMeta, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::root::<NdbEventMeta>(buf)
+}
+#[inline]
+/// Verifies that a buffer of bytes contains a size prefixed
+/// `NdbEventMeta` and returns it.
+/// Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `size_prefixed_root_as_ndb_event_meta_unchecked`.
+pub fn size_prefixed_root_as_ndb_event_meta(buf: &[u8]) -> Result<NdbEventMeta, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::size_prefixed_root::<NdbEventMeta>(buf)
+}
+#[inline]
+/// Verifies, with the given options, that a buffer of bytes
+/// contains a `NdbEventMeta` and returns it.
+/// Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `root_as_ndb_event_meta_unchecked`.
+pub fn root_as_ndb_event_meta_with_opts<'b, 'o>(
+ opts: &'o flatbuffers::VerifierOptions,
+ buf: &'b [u8],
+) -> Result<NdbEventMeta<'b>, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::root_with_opts::<NdbEventMeta<'b>>(opts, buf)
+}
+#[inline]
+/// Verifies, with the given verifier options, that a buffer of
+/// bytes contains a size prefixed `NdbEventMeta` and returns
+/// it. Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `root_as_ndb_event_meta_unchecked`.
+pub fn size_prefixed_root_as_ndb_event_meta_with_opts<'b, 'o>(
+ opts: &'o flatbuffers::VerifierOptions,
+ buf: &'b [u8],
+) -> Result<NdbEventMeta<'b>, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::size_prefixed_root_with_opts::<NdbEventMeta<'b>>(opts, buf)
+}
+#[inline]
+/// Assumes, without verification, that a buffer of bytes contains a NdbEventMeta and returns it.
+/// # Safety
+/// Callers must trust the given bytes do indeed contain a valid `NdbEventMeta`.
+pub unsafe fn root_as_ndb_event_meta_unchecked(buf: &[u8]) -> NdbEventMeta {
+ flatbuffers::root_unchecked::<NdbEventMeta>(buf)
+}
+#[inline]
+/// Assumes, without verification, that a buffer of bytes contains a size prefixed NdbEventMeta and returns it.
+/// # Safety
+/// Callers must trust the given bytes do indeed contain a valid size prefixed `NdbEventMeta`.
+pub unsafe fn size_prefixed_root_as_ndb_event_meta_unchecked(buf: &[u8]) -> NdbEventMeta {
+ flatbuffers::size_prefixed_root_unchecked::<NdbEventMeta>(buf)
+}
+#[inline]
+pub fn finish_ndb_event_meta_buffer<'a, 'b>(
+ fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ root: flatbuffers::WIPOffset<NdbEventMeta<'a>>) {
+ fbb.finish(root, None);
+}
+
+#[inline]
+pub fn finish_size_prefixed_ndb_event_meta_buffer<'a, 'b>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset<NdbEventMeta<'a>>) {
+ fbb.finish_size_prefixed(root, None);
+}
diff --git a/nostrdb/bindings/rust/ndb_profile.rs b/nostrdb/bindings/rust/ndb_profile.rs
@@ -0,0 +1,514 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+// @generated
+
+use core::mem;
+use core::cmp::Ordering;
+
+extern crate flatbuffers;
+use self::flatbuffers::{EndianScalar, Follow};
+
+pub enum NdbProfileOffset {}
+#[derive(Copy, Clone, PartialEq)]
+
+pub struct NdbProfile<'a> {
+ pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for NdbProfile<'a> {
+ type Inner = NdbProfile<'a>;
+ #[inline]
+ unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Self { _tab: flatbuffers::Table::new(buf, loc) }
+ }
+}
+
+impl<'a> NdbProfile<'a> {
+ pub const VT_NAME: flatbuffers::VOffsetT = 4;
+ pub const VT_WEBSITE: flatbuffers::VOffsetT = 6;
+ pub const VT_ABOUT: flatbuffers::VOffsetT = 8;
+ pub const VT_LUD16: flatbuffers::VOffsetT = 10;
+ pub const VT_BANNER: flatbuffers::VOffsetT = 12;
+ pub const VT_DISPLAY_NAME: flatbuffers::VOffsetT = 14;
+ pub const VT_REACTIONS: flatbuffers::VOffsetT = 16;
+ pub const VT_PICTURE: flatbuffers::VOffsetT = 18;
+ pub const VT_NIP05: flatbuffers::VOffsetT = 20;
+ pub const VT_DAMUS_DONATION: flatbuffers::VOffsetT = 22;
+ pub const VT_DAMUS_DONATION_V2: flatbuffers::VOffsetT = 24;
+ pub const VT_LUD06: flatbuffers::VOffsetT = 26;
+
+ #[inline]
+ pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+ NdbProfile { _tab: table }
+ }
+ #[allow(unused_mut)]
+ pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+ _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+ args: &'args NdbProfileArgs<'args>
+ ) -> flatbuffers::WIPOffset<NdbProfile<'bldr>> {
+ let mut builder = NdbProfileBuilder::new(_fbb);
+ if let Some(x) = args.lud06 { builder.add_lud06(x); }
+ builder.add_damus_donation_v2(args.damus_donation_v2);
+ builder.add_damus_donation(args.damus_donation);
+ if let Some(x) = args.nip05 { builder.add_nip05(x); }
+ if let Some(x) = args.picture { builder.add_picture(x); }
+ if let Some(x) = args.display_name { builder.add_display_name(x); }
+ if let Some(x) = args.banner { builder.add_banner(x); }
+ if let Some(x) = args.lud16 { builder.add_lud16(x); }
+ if let Some(x) = args.about { builder.add_about(x); }
+ if let Some(x) = args.website { builder.add_website(x); }
+ if let Some(x) = args.name { builder.add_name(x); }
+ builder.add_reactions(args.reactions);
+ builder.finish()
+ }
+
+
+ #[inline]
+ pub fn name(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_NAME, None)}
+ }
+ #[inline]
+ pub fn website(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_WEBSITE, None)}
+ }
+ #[inline]
+ pub fn about(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_ABOUT, None)}
+ }
+ #[inline]
+ pub fn lud16(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_LUD16, None)}
+ }
+ #[inline]
+ pub fn banner(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_BANNER, None)}
+ }
+ #[inline]
+ pub fn display_name(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_DISPLAY_NAME, None)}
+ }
+ #[inline]
+ pub fn reactions(&self) -> bool {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<bool>(NdbProfile::VT_REACTIONS, Some(true)).unwrap()}
+ }
+ #[inline]
+ pub fn picture(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_PICTURE, None)}
+ }
+ #[inline]
+ pub fn nip05(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_NIP05, None)}
+ }
+ #[inline]
+ pub fn damus_donation(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbProfile::VT_DAMUS_DONATION, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn damus_donation_v2(&self) -> i32 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<i32>(NdbProfile::VT_DAMUS_DONATION_V2, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn lud06(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfile::VT_LUD06, None)}
+ }
+}
+
+impl flatbuffers::Verifiable for NdbProfile<'_> {
+ #[inline]
+ fn run_verifier(
+ v: &mut flatbuffers::Verifier, pos: usize
+ ) -> Result<(), flatbuffers::InvalidFlatbuffer> {
+ use self::flatbuffers::Verifiable;
+ v.visit_table(pos)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("name", Self::VT_NAME, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("website", Self::VT_WEBSITE, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("about", Self::VT_ABOUT, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("lud16", Self::VT_LUD16, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("banner", Self::VT_BANNER, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("display_name", Self::VT_DISPLAY_NAME, false)?
+ .visit_field::<bool>("reactions", Self::VT_REACTIONS, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("picture", Self::VT_PICTURE, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("nip05", Self::VT_NIP05, false)?
+ .visit_field::<i32>("damus_donation", Self::VT_DAMUS_DONATION, false)?
+ .visit_field::<i32>("damus_donation_v2", Self::VT_DAMUS_DONATION_V2, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("lud06", Self::VT_LUD06, false)?
+ .finish();
+ Ok(())
+ }
+}
+pub struct NdbProfileArgs<'a> {
+ pub name: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub website: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub about: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub lud16: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub banner: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub display_name: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub reactions: bool,
+ pub picture: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub nip05: Option<flatbuffers::WIPOffset<&'a str>>,
+ pub damus_donation: i32,
+ pub damus_donation_v2: i32,
+ pub lud06: Option<flatbuffers::WIPOffset<&'a str>>,
+}
+impl<'a> Default for NdbProfileArgs<'a> {
+ #[inline]
+ fn default() -> Self {
+ NdbProfileArgs {
+ name: None,
+ website: None,
+ about: None,
+ lud16: None,
+ banner: None,
+ display_name: None,
+ reactions: true,
+ picture: None,
+ nip05: None,
+ damus_donation: 0,
+ damus_donation_v2: 0,
+ lud06: None,
+ }
+ }
+}
+
+pub struct NdbProfileBuilder<'a: 'b, 'b> {
+ fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
+}
+impl<'a: 'b, 'b> NdbProfileBuilder<'a, 'b> {
+ #[inline]
+ pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_NAME, name);
+ }
+ #[inline]
+ pub fn add_website(&mut self, website: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_WEBSITE, website);
+ }
+ #[inline]
+ pub fn add_about(&mut self, about: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_ABOUT, about);
+ }
+ #[inline]
+ pub fn add_lud16(&mut self, lud16: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_LUD16, lud16);
+ }
+ #[inline]
+ pub fn add_banner(&mut self, banner: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_BANNER, banner);
+ }
+ #[inline]
+ pub fn add_display_name(&mut self, display_name: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_DISPLAY_NAME, display_name);
+ }
+ #[inline]
+ pub fn add_reactions(&mut self, reactions: bool) {
+ self.fbb_.push_slot::<bool>(NdbProfile::VT_REACTIONS, reactions, true);
+ }
+ #[inline]
+ pub fn add_picture(&mut self, picture: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_PICTURE, picture);
+ }
+ #[inline]
+ pub fn add_nip05(&mut self, nip05: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_NIP05, nip05);
+ }
+ #[inline]
+ pub fn add_damus_donation(&mut self, damus_donation: i32) {
+ self.fbb_.push_slot::<i32>(NdbProfile::VT_DAMUS_DONATION, damus_donation, 0);
+ }
+ #[inline]
+ pub fn add_damus_donation_v2(&mut self, damus_donation_v2: i32) {
+ self.fbb_.push_slot::<i32>(NdbProfile::VT_DAMUS_DONATION_V2, damus_donation_v2, 0);
+ }
+ #[inline]
+ pub fn add_lud06(&mut self, lud06: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfile::VT_LUD06, lud06);
+ }
+ #[inline]
+ pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> NdbProfileBuilder<'a, 'b> {
+ let start = _fbb.start_table();
+ NdbProfileBuilder {
+ fbb_: _fbb,
+ start_: start,
+ }
+ }
+ #[inline]
+ pub fn finish(self) -> flatbuffers::WIPOffset<NdbProfile<'a>> {
+ let o = self.fbb_.end_table(self.start_);
+ flatbuffers::WIPOffset::new(o.value())
+ }
+}
+
+impl core::fmt::Debug for NdbProfile<'_> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let mut ds = f.debug_struct("NdbProfile");
+ ds.field("name", &self.name());
+ ds.field("website", &self.website());
+ ds.field("about", &self.about());
+ ds.field("lud16", &self.lud16());
+ ds.field("banner", &self.banner());
+ ds.field("display_name", &self.display_name());
+ ds.field("reactions", &self.reactions());
+ ds.field("picture", &self.picture());
+ ds.field("nip05", &self.nip05());
+ ds.field("damus_donation", &self.damus_donation());
+ ds.field("damus_donation_v2", &self.damus_donation_v2());
+ ds.field("lud06", &self.lud06());
+ ds.finish()
+ }
+}
+pub enum NdbProfileRecordOffset {}
+#[derive(Copy, Clone, PartialEq)]
+
+pub struct NdbProfileRecord<'a> {
+ pub _tab: flatbuffers::Table<'a>,
+}
+
+impl<'a> flatbuffers::Follow<'a> for NdbProfileRecord<'a> {
+ type Inner = NdbProfileRecord<'a>;
+ #[inline]
+ unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {
+ Self { _tab: flatbuffers::Table::new(buf, loc) }
+ }
+}
+
+impl<'a> NdbProfileRecord<'a> {
+ pub const VT_PROFILE: flatbuffers::VOffsetT = 4;
+ pub const VT_RECEIVED_AT: flatbuffers::VOffsetT = 6;
+ pub const VT_NOTE_KEY: flatbuffers::VOffsetT = 8;
+ pub const VT_LNURL: flatbuffers::VOffsetT = 10;
+
+ #[inline]
+ pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self {
+ NdbProfileRecord { _tab: table }
+ }
+ #[allow(unused_mut)]
+ pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(
+ _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,
+ args: &'args NdbProfileRecordArgs<'args>
+ ) -> flatbuffers::WIPOffset<NdbProfileRecord<'bldr>> {
+ let mut builder = NdbProfileRecordBuilder::new(_fbb);
+ builder.add_note_key(args.note_key);
+ builder.add_received_at(args.received_at);
+ if let Some(x) = args.lnurl { builder.add_lnurl(x); }
+ if let Some(x) = args.profile { builder.add_profile(x); }
+ builder.finish()
+ }
+
+
+ #[inline]
+ pub fn profile(&self) -> Option<NdbProfile<'a>> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<NdbProfile>>(NdbProfileRecord::VT_PROFILE, None)}
+ }
+ #[inline]
+ pub fn received_at(&self) -> u64 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<u64>(NdbProfileRecord::VT_RECEIVED_AT, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn note_key(&self) -> u64 {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<u64>(NdbProfileRecord::VT_NOTE_KEY, Some(0)).unwrap()}
+ }
+ #[inline]
+ pub fn lnurl(&self) -> Option<&'a str> {
+ // Safety:
+ // Created from valid Table for this object
+ // which contains a valid value in this slot
+ unsafe { self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(NdbProfileRecord::VT_LNURL, None)}
+ }
+}
+
+impl flatbuffers::Verifiable for NdbProfileRecord<'_> {
+ #[inline]
+ fn run_verifier(
+ v: &mut flatbuffers::Verifier, pos: usize
+ ) -> Result<(), flatbuffers::InvalidFlatbuffer> {
+ use self::flatbuffers::Verifiable;
+ v.visit_table(pos)?
+ .visit_field::<flatbuffers::ForwardsUOffset<NdbProfile>>("profile", Self::VT_PROFILE, false)?
+ .visit_field::<u64>("received_at", Self::VT_RECEIVED_AT, false)?
+ .visit_field::<u64>("note_key", Self::VT_NOTE_KEY, false)?
+ .visit_field::<flatbuffers::ForwardsUOffset<&str>>("lnurl", Self::VT_LNURL, false)?
+ .finish();
+ Ok(())
+ }
+}
+pub struct NdbProfileRecordArgs<'a> {
+ pub profile: Option<flatbuffers::WIPOffset<NdbProfile<'a>>>,
+ pub received_at: u64,
+ pub note_key: u64,
+ pub lnurl: Option<flatbuffers::WIPOffset<&'a str>>,
+}
+impl<'a> Default for NdbProfileRecordArgs<'a> {
+ #[inline]
+ fn default() -> Self {
+ NdbProfileRecordArgs {
+ profile: None,
+ received_at: 0,
+ note_key: 0,
+ lnurl: None,
+ }
+ }
+}
+
+pub struct NdbProfileRecordBuilder<'a: 'b, 'b> {
+ fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ start_: flatbuffers::WIPOffset<flatbuffers::TableUnfinishedWIPOffset>,
+}
+impl<'a: 'b, 'b> NdbProfileRecordBuilder<'a, 'b> {
+ #[inline]
+ pub fn add_profile(&mut self, profile: flatbuffers::WIPOffset<NdbProfile<'b >>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<NdbProfile>>(NdbProfileRecord::VT_PROFILE, profile);
+ }
+ #[inline]
+ pub fn add_received_at(&mut self, received_at: u64) {
+ self.fbb_.push_slot::<u64>(NdbProfileRecord::VT_RECEIVED_AT, received_at, 0);
+ }
+ #[inline]
+ pub fn add_note_key(&mut self, note_key: u64) {
+ self.fbb_.push_slot::<u64>(NdbProfileRecord::VT_NOTE_KEY, note_key, 0);
+ }
+ #[inline]
+ pub fn add_lnurl(&mut self, lnurl: flatbuffers::WIPOffset<&'b str>) {
+ self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>(NdbProfileRecord::VT_LNURL, lnurl);
+ }
+ #[inline]
+ pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> NdbProfileRecordBuilder<'a, 'b> {
+ let start = _fbb.start_table();
+ NdbProfileRecordBuilder {
+ fbb_: _fbb,
+ start_: start,
+ }
+ }
+ #[inline]
+ pub fn finish(self) -> flatbuffers::WIPOffset<NdbProfileRecord<'a>> {
+ let o = self.fbb_.end_table(self.start_);
+ flatbuffers::WIPOffset::new(o.value())
+ }
+}
+
+impl core::fmt::Debug for NdbProfileRecord<'_> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let mut ds = f.debug_struct("NdbProfileRecord");
+ ds.field("profile", &self.profile());
+ ds.field("received_at", &self.received_at());
+ ds.field("note_key", &self.note_key());
+ ds.field("lnurl", &self.lnurl());
+ ds.finish()
+ }
+}
+#[inline]
+/// Verifies that a buffer of bytes contains a `NdbProfileRecord`
+/// and returns it.
+/// Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `root_as_ndb_profile_record_unchecked`.
+pub fn root_as_ndb_profile_record(buf: &[u8]) -> Result<NdbProfileRecord, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::root::<NdbProfileRecord>(buf)
+}
+#[inline]
+/// Verifies that a buffer of bytes contains a size prefixed
+/// `NdbProfileRecord` and returns it.
+/// Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `size_prefixed_root_as_ndb_profile_record_unchecked`.
+pub fn size_prefixed_root_as_ndb_profile_record(buf: &[u8]) -> Result<NdbProfileRecord, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::size_prefixed_root::<NdbProfileRecord>(buf)
+}
+#[inline]
+/// Verifies, with the given options, that a buffer of bytes
+/// contains a `NdbProfileRecord` and returns it.
+/// Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `root_as_ndb_profile_record_unchecked`.
+pub fn root_as_ndb_profile_record_with_opts<'b, 'o>(
+ opts: &'o flatbuffers::VerifierOptions,
+ buf: &'b [u8],
+) -> Result<NdbProfileRecord<'b>, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::root_with_opts::<NdbProfileRecord<'b>>(opts, buf)
+}
+#[inline]
+/// Verifies, with the given verifier options, that a buffer of
+/// bytes contains a size prefixed `NdbProfileRecord` and returns
+/// it. Note that verification is still experimental and may not
+/// catch every error, or be maximally performant. For the
+/// previous, unchecked, behavior use
+/// `root_as_ndb_profile_record_unchecked`.
+pub fn size_prefixed_root_as_ndb_profile_record_with_opts<'b, 'o>(
+ opts: &'o flatbuffers::VerifierOptions,
+ buf: &'b [u8],
+) -> Result<NdbProfileRecord<'b>, flatbuffers::InvalidFlatbuffer> {
+ flatbuffers::size_prefixed_root_with_opts::<NdbProfileRecord<'b>>(opts, buf)
+}
+#[inline]
+/// Assumes, without verification, that a buffer of bytes contains a NdbProfileRecord and returns it.
+/// # Safety
+/// Callers must trust the given bytes do indeed contain a valid `NdbProfileRecord`.
+pub unsafe fn root_as_ndb_profile_record_unchecked(buf: &[u8]) -> NdbProfileRecord {
+ flatbuffers::root_unchecked::<NdbProfileRecord>(buf)
+}
+#[inline]
+/// Assumes, without verification, that a buffer of bytes contains a size prefixed NdbProfileRecord and returns it.
+/// # Safety
+/// Callers must trust the given bytes do indeed contain a valid size prefixed `NdbProfileRecord`.
+pub unsafe fn size_prefixed_root_as_ndb_profile_record_unchecked(buf: &[u8]) -> NdbProfileRecord {
+ flatbuffers::size_prefixed_root_unchecked::<NdbProfileRecord>(buf)
+}
+#[inline]
+pub fn finish_ndb_profile_record_buffer<'a, 'b>(
+ fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,
+ root: flatbuffers::WIPOffset<NdbProfileRecord<'a>>) {
+ fbb.finish(root, None);
+}
+
+#[inline]
+pub fn finish_size_prefixed_ndb_profile_record_buffer<'a, 'b>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset<NdbProfileRecord<'a>>) {
+ fbb.finish_size_prefixed(root, None);
+}
diff --git a/nostrdb/bindings/swift/NdbProfile.swift b/nostrdb/bindings/swift/NdbProfile.swift
@@ -2,7 +2,7 @@
// swiftlint:disable all
// swiftformat:disable all
-
+import FlatBuffers
public struct NdbProfile: FlatBufferObject, Verifiable {
diff --git a/nostrdb/bolt11/bech32.c b/nostrdb/bolt11/bech32.c
@@ -0,0 +1,217 @@
+/* Stolen from https://github.com/sipa/bech32/blob/master/ref/c/segwit_addr.c,
+ * with only the two ' > 90' checks hoisted, and more internals exposed */
+
+/* Copyright (c) 2017, 2021 Pieter Wuille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "../config.h"
+#include <assert.h>
+#include "bech32.h"
+#include <string.h>
+
+static uint32_t bech32_polymod_step(uint32_t pre) {
+ uint8_t b = pre >> 25;
+ return ((pre & 0x1FFFFFF) << 5) ^
+ (-((b >> 0) & 1) & 0x3b6a57b2UL) ^
+ (-((b >> 1) & 1) & 0x26508e6dUL) ^
+ (-((b >> 2) & 1) & 0x1ea119faUL) ^
+ (-((b >> 3) & 1) & 0x3d4233ddUL) ^
+ (-((b >> 4) & 1) & 0x2a1462b3UL);
+}
+
+static uint32_t bech32_final_constant(bech32_encoding enc) {
+ if (enc == BECH32_ENCODING_BECH32) return 1;
+ if (enc == BECH32_ENCODING_BECH32M) return 0x2bc830a3;
+ assert(0);
+}
+
+const char bech32_charset[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
+
+const int8_t bech32_charset_rev[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
+ -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
+ 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
+ -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
+ 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
+};
+
+int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, size_t max_input_len, bech32_encoding enc) {
+ uint32_t chk = 1;
+ size_t i = 0;
+ while (hrp[i] != 0) {
+ int ch = hrp[i];
+ if (ch < 33 || ch > 126) {
+ return 0;
+ }
+
+ if (ch >= 'A' && ch <= 'Z') return 0;
+ chk = bech32_polymod_step(chk) ^ (ch >> 5);
+ ++i;
+ }
+ if (i + 7 + data_len > max_input_len) return 0;
+ chk = bech32_polymod_step(chk);
+ while (*hrp != 0) {
+ chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f);
+ *(output++) = *(hrp++);
+ }
+ *(output++) = '1';
+ for (i = 0; i < data_len; ++i) {
+ if (*data >> 5) return 0;
+ chk = bech32_polymod_step(chk) ^ (*data);
+ *(output++) = bech32_charset[*(data++)];
+ }
+ for (i = 0; i < 6; ++i) {
+ chk = bech32_polymod_step(chk);
+ }
+ chk ^= bech32_final_constant(enc);
+ for (i = 0; i < 6; ++i) {
+ *(output++) = bech32_charset[(chk >> ((5 - i) * 5)) & 0x1f];
+ }
+ *output = 0;
+ return 1;
+}
+
+bech32_encoding bech32_decode_len(char* hrp, uint8_t *data, size_t *data_len, const char *input, size_t input_len) {
+ uint32_t chk = 1;
+ size_t i;
+ size_t hrp_len;
+ int have_lower = 0, have_upper = 0;
+ if (input_len < 8) {
+ return BECH32_ENCODING_NONE;
+ }
+ *data_len = 0;
+ while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') {
+ ++(*data_len);
+ }
+ hrp_len = input_len - (1 + *data_len);
+ if (1 + *data_len >= input_len || *data_len < 6) {
+ return BECH32_ENCODING_NONE;
+ }
+ *(data_len) -= 6;
+ for (i = 0; i < hrp_len; ++i) {
+ int ch = input[i];
+ if (ch < 33 || ch > 126) {
+ return BECH32_ENCODING_NONE;
+ }
+ if (ch >= 'a' && ch <= 'z') {
+ have_lower = 1;
+ } else if (ch >= 'A' && ch <= 'Z') {
+ have_upper = 1;
+ ch = (ch - 'A') + 'a';
+ }
+ hrp[i] = ch;
+ chk = bech32_polymod_step(chk) ^ (ch >> 5);
+ }
+ hrp[i] = 0;
+ chk = bech32_polymod_step(chk);
+ for (i = 0; i < hrp_len; ++i) {
+ chk = bech32_polymod_step(chk) ^ (input[i] & 0x1f);
+ }
+ ++i;
+ while (i < input_len) {
+ int v = (input[i] & 0x80) ? -1 : bech32_charset_rev[(int)input[i]];
+ if (input[i] >= 'a' && input[i] <= 'z') have_lower = 1;
+ if (input[i] >= 'A' && input[i] <= 'Z') have_upper = 1;
+ if (v == -1) {
+ return BECH32_ENCODING_NONE;
+ }
+ chk = bech32_polymod_step(chk) ^ v;
+ if (i + 6 < input_len) {
+ data[i - (1 + hrp_len)] = v;
+ }
+ ++i;
+ }
+ if (have_lower && have_upper) {
+ return BECH32_ENCODING_NONE;
+ }
+ if (chk == bech32_final_constant(BECH32_ENCODING_BECH32)) {
+ return BECH32_ENCODING_BECH32;
+ } else if (chk == bech32_final_constant(BECH32_ENCODING_BECH32M)) {
+ return BECH32_ENCODING_BECH32M;
+ } else {
+ return BECH32_ENCODING_NONE;
+ }
+}
+
+bech32_encoding bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input, size_t max_input_len) {
+ size_t len = strlen(input);
+ if (len > max_input_len) {
+ return BECH32_ENCODING_NONE;
+ }
+ return bech32_decode_len(hrp, data, data_len, input, len);
+}
+
+int bech32_convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) {
+ uint32_t val = 0;
+ int bits = 0;
+ uint32_t maxv = (((uint32_t)1) << outbits) - 1;
+ while (inlen--) {
+ val = (val << inbits) | *(in++);
+ bits += inbits;
+ while (bits >= outbits) {
+ bits -= outbits;
+ out[(*outlen)++] = (val >> bits) & maxv;
+ }
+ }
+ if (pad) {
+ if (bits) {
+ out[(*outlen)++] = (val << (outbits - bits)) & maxv;
+ }
+ } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) {
+ return 0;
+ }
+ return 1;
+}
+
+int segwit_addr_encode(char *output, const char *hrp, int witver, const uint8_t *witprog, size_t witprog_len) {
+ uint8_t data[65];
+ size_t datalen = 0;
+ bech32_encoding enc = BECH32_ENCODING_BECH32;
+ if (witver > 16) return 0;
+ if (witver == 0 && witprog_len != 20 && witprog_len != 32) return 0;
+ if (witprog_len < 2 || witprog_len > 40) return 0;
+ if (witver > 0) enc = BECH32_ENCODING_BECH32M;
+ data[0] = witver;
+ bech32_convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1);
+ ++datalen;
+ return bech32_encode(output, hrp, data, datalen, 90, enc);
+}
+
+int segwit_addr_decode(int* witver, uint8_t* witdata, size_t* witdata_len, const char* hrp, const char* addr) {
+ uint8_t data[84];
+ char hrp_actual[84];
+ size_t data_len;
+ bech32_encoding enc = bech32_decode(hrp_actual, data, &data_len, addr, 90);
+ if (enc == BECH32_ENCODING_NONE) return 0;
+ if (data_len == 0 || data_len > 65) return 0;
+ if (strncmp(hrp, hrp_actual, 84) != 0) return 0;
+ if (data[0] > 16) return 0;
+ if (data[0] == 0 && enc != BECH32_ENCODING_BECH32) return 0;
+ if (data[0] > 0 && enc != BECH32_ENCODING_BECH32M) return 0;
+ *witdata_len = 0;
+ if (!bech32_convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) return 0;
+ if (*witdata_len < 2 || *witdata_len > 40) return 0;
+ if (data[0] == 0 && *witdata_len != 20 && *witdata_len != 32) return 0;
+ *witver = data[0];
+ return 1;
+}
diff --git a/nostrdb/bolt11/bech32.h b/nostrdb/bolt11/bech32.h
@@ -0,0 +1,142 @@
+/* Stolen from https://github.com/sipa/bech32/blob/master/ref/c/segwit_addr.h,
+ * with only the two ' > 90' checks hoisted */
+
+/* Copyright (c) 2017, 2021 Pieter Wuille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef LIGHTNING_COMMON_BECH32_H
+#define LIGHTNING_COMMON_BECH32_H
+#include "../config.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/** Encode a SegWit address
+ *
+ * Out: output: Pointer to a buffer of size 73 + strlen(hrp) that will be
+ * updated to contain the null-terminated address.
+ * In: hrp: Pointer to the null-terminated human readable part to use
+ * (chain/network specific).
+ * ver: Version of the witness program (between 0 and 16 inclusive).
+ * prog: Data bytes for the witness program (between 2 and 40 bytes).
+ * prog_len: Number of data bytes in prog.
+ * Returns 1 if successful.
+ */
+int segwit_addr_encode(
+ char *output,
+ const char *hrp,
+ int ver,
+ const uint8_t *prog,
+ size_t prog_len
+);
+
+/** Decode a SegWit address
+ *
+ * Out: ver: Pointer to an int that will be updated to contain the witness
+ * program version (between 0 and 16 inclusive).
+ * prog: Pointer to a buffer of size 40 that will be updated to
+ * contain the witness program bytes.
+ * prog_len: Pointer to a size_t that will be updated to contain the length
+ * of bytes in prog.
+ * hrp: Pointer to the null-terminated human readable part that is
+ * expected (chain/network specific).
+ * addr: Pointer to the null-terminated address.
+ * Returns 1 if successful.
+ */
+int segwit_addr_decode(
+ int* ver,
+ uint8_t* prog,
+ size_t* prog_len,
+ const char* hrp,
+ const char* addr
+);
+
+/** Supported encodings. */
+typedef enum {
+ BECH32_ENCODING_NONE,
+ BECH32_ENCODING_BECH32,
+ BECH32_ENCODING_BECH32M
+} bech32_encoding;
+
+/** Encode a Bech32 or Bech32m string
+ *
+ * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that
+ * will be updated to contain the null-terminated Bech32 string.
+ * In: hrp : Pointer to the null-terminated human readable part.
+ * data : Pointer to an array of 5-bit values.
+ * data_len: Length of the data array.
+ * max_input_len: Maximum valid length of input (90 for segwit usage).
+ * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}).
+ * Returns 1 if successful.
+ */
+int bech32_encode(
+ char *output,
+ const char *hrp,
+ const uint8_t *data,
+ size_t data_len,
+ size_t max_input_len,
+ bech32_encoding enc
+);
+
+/** Decode a Bech32 or Bech32m string
+ *
+ * Out: hrp: Pointer to a buffer of size strlen(input) - 6. Will be
+ * updated to contain the null-terminated human readable part.
+ * data: Pointer to a buffer of size strlen(input) - 8 that will
+ * hold the encoded 5-bit data values.
+ * data_len: Pointer to a size_t that will be updated to be the number
+ * of entries in data.
+ * In: input: Pointer to a null-terminated Bech32 string.
+ * max_input_len: Maximum valid length of input (90 for segwit usage).
+ * Returns BECH32_ENCODING_BECH32{,M} to indicate decoding was successful
+ * with the specified encoding standard. BECH32_ENCODING_NONE is returned if
+ * decoding failed.
+ */
+bech32_encoding bech32_decode(
+ char *hrp,
+ uint8_t *data,
+ size_t *data_len,
+ const char *input,
+ size_t max_input_len
+);
+
+bech32_encoding bech32_decode_len(
+ char *hrp,
+ uint8_t *data,
+ size_t *data_len,
+ const char *input,
+ size_t input_len
+);
+
+/* Helper from bech32: translates inbits-bit bytes to outbits-bit bytes.
+ * @outlen is incremented as bytes are added.
+ * @pad is true if we're to pad, otherwise truncate last byte if necessary
+ */
+int bech32_convert_bits(uint8_t* out, size_t* outlen, int outbits,
+ const uint8_t* in, size_t inlen, int inbits,
+ int pad);
+
+/* The charset, and reverse mapping */
+extern const char bech32_charset[32];
+extern const int8_t bech32_charset_rev[128];
+
+#endif /* LIGHTNING_COMMON_BECH32_H */
+
diff --git a/nostrdb/config.h b/nostrdb/config.h
@@ -12,7 +12,7 @@
#define HAVE_UNALIGNED_ACCESS 1
#define HAVE_TYPEOF 1
#define HAVE_BIG_ENDIAN 0
-#define HAVE_BYTESWAP_H 0
-#define HAVE_BSWAP_64 0
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
#define HAVE_LITTLE_ENDIAN 1
#endif /* CCAN_CONFIG_H */
diff --git a/nostrdb/configurator.c b/nostrdb/configurator.c
@@ -0,0 +1,1110 @@
+/* Simple tool to create config.h.
+ * Would be much easier with ccan modules, but deliberately standalone.
+ *
+ * Copyright 2011 Rusty Russell <rusty@rustcorp.com.au>. MIT license.
+ *
+ * c12r_err, c12r_errx functions copied from ccan/err/err.c
+ * Copyright Rusty Russell <rusty@rustcorp.com.au>. CC0 (Public domain) License.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define _POSIX_C_SOURCE 200809L /* For pclose, popen, strdup */
+
+#define EXIT_BAD_USAGE 1
+#define EXIT_TROUBLE_RUNNING 2
+#define EXIT_BAD_TEST 3
+#define EXIT_BAD_INPUT 4
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef _MSC_VER
+#define popen _popen
+#define pclose _pclose
+#endif
+
+#ifdef _MSC_VER
+#define DEFAULT_COMPILER "cl"
+/* Note: Dash options avoid POSIX path conversion when used under msys bash
+ * and are therefore preferred to slash (e.g. -nologo over /nologo)
+ * Note: Disable Warning 4200 "nonstandard extension used : zero-sized array
+ * in struct/union" for flexible array members.
+ */
+#define DEFAULT_FLAGS "-nologo -Zi -W4 -wd4200 " \
+ "-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS"
+#define DEFAULT_OUTPUT_EXE_FLAG "-Fe:"
+#else
+#define DEFAULT_COMPILER "cc"
+#define DEFAULT_FLAGS "-g3 -ggdb -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition"
+#define DEFAULT_OUTPUT_EXE_FLAG "-o"
+#endif
+
+#define OUTPUT_FILE "configurator.out"
+#define INPUT_FILE "configuratortest.c"
+
+#ifdef _WIN32
+#define DIR_SEP "\\"
+#else
+#define DIR_SEP "/"
+#endif
+
+static const char *progname = "";
+static int verbose;
+static bool like_a_libtool = false;
+
+struct test {
+ const char *name;
+ const char *desc;
+ /*
+ * Template style flags (pick one):
+ * OUTSIDE_MAIN:
+ * - put a simple boilerplate main below it.
+ * DEFINES_FUNC:
+ * - defines a static function called func; adds ref to avoid warnings
+ * INSIDE_MAIN:
+ * - put this inside main().
+ * DEFINES_EVERYTHING:
+ * - don't add any boilerplate at all.
+ *
+ * Execution flags:
+ * EXECUTE:
+ * - a runtime test; must compile, exit 0 means flag is set.
+ * MAY_NOT_COMPILE:
+ * - Only useful with EXECUTE: don't get upset if it doesn't compile.
+ * <nothing>:
+ * - a compile test, if it compiles must run and exit 0.
+ */
+ const char *style;
+ const char *depends;
+ const char *link;
+ const char *fragment;
+ const char *flags;
+ const char *overrides; /* On success, force this to '1' */
+ bool done;
+ bool answer;
+};
+
+/* Terminated by a NULL name */
+static struct test *tests;
+
+static const struct test base_tests[] = {
+ { "HAVE_UNALIGNED_ACCESS", "unaligned access to int",
+ "DEFINES_EVERYTHING|EXECUTE", NULL, NULL,
+ "#include <string.h>\n"
+ "int main(int argc, char *argv[]) {\n"
+ " (void)argc;\n"
+ " char pad[sizeof(int *) * 1];\n"
+ " memcpy(pad, argv[0], sizeof(pad));\n"
+ " int *x = (int *)pad, *y = (int *)(pad + 1);\n"
+ " return *x == *y;\n"
+ "}\n" },
+ { "HAVE_TYPEOF", "__typeof__ support",
+ "INSIDE_MAIN", NULL, NULL,
+ "__typeof__(argc) i; i = argc; return i == argc ? 0 : 1;" },
+ { "HAVE_BIG_ENDIAN", "big endian",
+ "INSIDE_MAIN|EXECUTE", NULL, NULL,
+ "union { int i; char c[sizeof(int)]; } u;\n"
+ "u.i = 0x01020304;\n"
+ "return u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 ? 0 : 1;" },
+ { "HAVE_BYTESWAP_H", "<byteswap.h>",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "#include <byteswap.h>\n" },
+ { "HAVE_BSWAP_64", "bswap64 in byteswap.h",
+ "DEFINES_FUNC", "HAVE_BYTESWAP_H", NULL,
+ "#include <byteswap.h>\n"
+ "static int func(int x) { return bswap_64(x); }" },
+ { "HAVE_LITTLE_ENDIAN", "little endian",
+ "INSIDE_MAIN|EXECUTE", NULL, NULL,
+ "union { int i; char c[sizeof(int)]; } u;\n"
+ "u.i = 0x01020304;\n"
+ "return u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01 ? 0 : 1;" },
+ /*
+ { "HAVE_32BIT_OFF_T", "off_t is 32 bits",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL,
+ "#include <sys/types.h>\n"
+ "int main(void) {\n"
+ " return sizeof(off_t) == 4 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_ALIGNOF", "__alignof__ support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __alignof__(double) > 0 ? 0 : 1;" },
+ { "HAVE_ASPRINTF", "asprintf() declaration",
+ "DEFINES_FUNC", NULL, NULL,
+ "#ifndef _GNU_SOURCE\n"
+ "#define _GNU_SOURCE\n"
+ "#endif\n"
+ "#include <stdio.h>\n"
+ "static char *func(int x) {"
+ " char *p;\n"
+ " if (asprintf(&p, \"%u\", x) == -1) \n"
+ " p = NULL;\n"
+ " return p;\n"
+ "}" },
+ { "HAVE_ATTRIBUTE_COLD", "__attribute__((cold)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static int __attribute__((cold)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_CONST", "__attribute__((const)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static int __attribute__((const)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_DEPRECATED", "__attribute__((deprecated)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static int __attribute__((deprecated)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static char *__attribute__((nonnull)) func(char *p) { return p; }" },
+ { "HAVE_ATTRIBUTE_RETURNS_NONNULL", "__attribute__((returns_nonnull)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static const char *__attribute__((returns_nonnull)) func(void) { return \"hi\"; }" },
+ { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static int __attribute__((sentinel)) func(int i, ...) { return i; }" },
+ { "HAVE_ATTRIBUTE_PURE", "__attribute__((pure)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static int __attribute__((pure)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_MAY_ALIAS", "__attribute__((may_alias)) support",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "typedef short __attribute__((__may_alias__)) short_a;" },
+ { "HAVE_ATTRIBUTE_NORETURN", "__attribute__((noreturn)) support",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <stdlib.h>\n"
+ "static void __attribute__((noreturn)) func(int x) { exit(x); }" },
+ { "HAVE_ATTRIBUTE_PRINTF", "__attribute__ format printf support",
+ "DEFINES_FUNC", NULL, NULL,
+ "static void __attribute__((format(__printf__, 1, 2))) func(const char *fmt, ...) { (void)fmt; }" },
+ { "HAVE_ATTRIBUTE_UNUSED", "__attribute__((unused)) support",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "static int __attribute__((unused)) func(int x) { return x; }" },
+ { "HAVE_ATTRIBUTE_USED", "__attribute__((used)) support",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "static int __attribute__((used)) func(int x) { return x; }" },
+ { "HAVE_BACKTRACE", "backtrace() in <execinfo.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <execinfo.h>\n"
+ "static int func(int x) {"
+ " void *bt[10];\n"
+ " return backtrace(bt, 10) < x;\n"
+ "}" },
+ { "HAVE_BUILTIN_CHOOSE_EXPR", "__builtin_choose_expr support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_choose_expr(1, 0, \"garbage\");" },
+ { "HAVE_BUILTIN_CLZ", "__builtin_clz support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_clz(1) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CLZL", "__builtin_clzl support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_clzl(1) == (sizeof(long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CLZLL", "__builtin_clzll support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CTZ", "__builtin_ctz support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_ctz(1 << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CTZL", "__builtin_ctzl support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_ctzl(1UL << (sizeof(long)*8 - 1)) == (sizeof(long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CTZLL", "__builtin_ctzll support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_ctzll(1ULL << (sizeof(long long)*8 - 1)) == (sizeof(long long)*8 - 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_CONSTANT_P", "__builtin_constant_p support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_constant_p(1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_EXPECT", "__builtin_expect support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_expect(argc == 1, 1) ? 0 : 1;" },
+ { "HAVE_BUILTIN_FFS", "__builtin_ffs support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_ffs(0) == 0 ? 0 : 1;" },
+ { "HAVE_BUILTIN_FFSL", "__builtin_ffsl support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_ffsl(0L) == 0 ? 0 : 1;" },
+ { "HAVE_BUILTIN_FFSLL", "__builtin_ffsll support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_ffsll(0LL) == 0 ? 0 : 1;" },
+ { "HAVE_BUILTIN_POPCOUNT", "__builtin_popcount support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_popcount(255) == 8 ? 0 : 1;" },
+ { "HAVE_BUILTIN_POPCOUNTL", "__builtin_popcountl support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_popcountl(255L) == 8 ? 0 : 1;" },
+ { "HAVE_BUILTIN_POPCOUNTLL", "__builtin_popcountll support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_popcountll(255LL) == 8 ? 0 : 1;" },
+ { "HAVE_BUILTIN_TYPES_COMPATIBLE_P", "__builtin_types_compatible_p support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return __builtin_types_compatible_p(char *, int) ? 1 : 0;" },
+ { "HAVE_ICCARM_INTRINSICS", "<intrinsics.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <intrinsics.h>\n"
+ "int func(int v) {\n"
+ " return __CLZ(__RBIT(v));\n"
+ "}" },
+ { "HAVE_CLOCK_GETTIME", "clock_gettime() declaration",
+ "DEFINES_FUNC", "HAVE_STRUCT_TIMESPEC", NULL,
+ "#include <time.h>\n"
+ "static struct timespec func(void) {\n"
+ " struct timespec ts;\n"
+ " clock_gettime(CLOCK_REALTIME, &ts);\n"
+ " return ts;\n"
+ "}\n" },
+ { "HAVE_CLOCK_GETTIME_IN_LIBRT", "clock_gettime() in librt",
+ "DEFINES_FUNC",
+ "HAVE_STRUCT_TIMESPEC !HAVE_CLOCK_GETTIME",
+ "-lrt",
+ "#include <time.h>\n"
+ "static struct timespec func(void) {\n"
+ " struct timespec ts;\n"
+ " clock_gettime(CLOCK_REALTIME, &ts);\n"
+ " return ts;\n"
+ "}\n",
+ "HAVE_CLOCK_GETTIME" },
+ { "HAVE_COMPOUND_LITERALS", "compound literal support",
+ "INSIDE_MAIN", NULL, NULL,
+ "int *foo = (int[]) { 1, 2, 3, 4 };\n"
+ "return foo[0] ? 0 : 1;" },
+ { "HAVE_FCHDIR", "fchdir support",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <sys/stat.h>\n"
+ "#include <fcntl.h>\n"
+ "#include <unistd.h>\n"
+ "int main(void) {\n"
+ " int fd = open(\"..\", O_RDONLY);\n"
+ " return fchdir(fd) == 0 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_ERR_H", "<err.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <err.h>\n"
+ "static void func(int arg) {\n"
+ " if (arg == 0)\n"
+ " err(1, \"err %u\", arg);\n"
+ " if (arg == 1)\n"
+ " errx(1, \"err %u\", arg);\n"
+ " if (arg == 3)\n"
+ " warn(\"warn %u\", arg);\n"
+ " if (arg == 4)\n"
+ " warnx(\"warn %u\", arg);\n"
+ "}\n" },
+ { "HAVE_FILE_OFFSET_BITS", "_FILE_OFFSET_BITS to get 64-bit offsets",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE",
+ "HAVE_32BIT_OFF_T", NULL,
+ "#define _FILE_OFFSET_BITS 64\n"
+ "#include <sys/types.h>\n"
+ "int main(void) {\n"
+ " return sizeof(off_t) == 8 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_FOR_LOOP_DECLARATION", "for loop declaration support",
+ "INSIDE_MAIN", NULL, NULL,
+ "int ret = 1;\n"
+ "for (int i = 0; i < argc; i++) { ret = 0; };\n"
+ "return ret;" },
+ { "HAVE_FLEXIBLE_ARRAY_MEMBER", "flexible array member support",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "struct foo { unsigned int x; int arr[]; };" },
+ { "HAVE_GETPAGESIZE", "getpagesize() in <unistd.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <unistd.h>\n"
+ "static int func(void) { return getpagesize(); }" },
+ { "HAVE_ISBLANK", "isblank() in <ctype.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#ifndef _GNU_SOURCE\n"
+ "#define _GNU_SOURCE\n"
+ "#endif\n"
+ "#include <ctype.h>\n"
+ "static int func(void) { return isblank(' '); }" },
+ { "HAVE_MEMMEM", "memmem in <string.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#ifndef _GNU_SOURCE\n"
+ "#define _GNU_SOURCE\n"
+ "#endif\n"
+ "#include <string.h>\n"
+ "static void *func(void *h, size_t hl, void *n, size_t nl) {\n"
+ "return memmem(h, hl, n, nl);"
+ "}\n", },
+ { "HAVE_MEMRCHR", "memrchr in <string.h>",
+ "DEFINES_FUNC", NULL, NULL,
+ "#ifndef _GNU_SOURCE\n"
+ "#define _GNU_SOURCE\n"
+ "#endif\n"
+ "#include <string.h>\n"
+ "static void *func(void *s, int c, size_t n) {\n"
+ "return memrchr(s, c, n);"
+ "}\n", },
+ { "HAVE_MMAP", "mmap() declaration",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <sys/mman.h>\n"
+ "static void *func(int fd) {\n"
+ " return mmap(0, 65536, PROT_READ, MAP_SHARED, fd, 0);\n"
+ "}" },
+ { "HAVE_PROC_SELF_MAPS", "/proc/self/maps exists",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <sys/stat.h>\n"
+ "#include <fcntl.h>\n"
+ "int main(void) {\n"
+ " return open(\"/proc/self/maps\", O_RDONLY) != -1 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_QSORT_R_PRIVATE_LAST", "qsort_r cmp takes trailing arg",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL,
+ "#ifndef _GNU_SOURCE\n"
+ "#define _GNU_SOURCE\n"
+ "#endif\n"
+ "#include <stdlib.h>\n"
+ "static int cmp(const void *lp, const void *rp, void *priv) {\n"
+ " *(unsigned int *)priv = 1;\n"
+ " return *(const int *)lp - *(const int *)rp; }\n"
+ "int main(void) {\n"
+ " int array[] = { 9, 2, 5 };\n"
+ " unsigned int called = 0;\n"
+ " qsort_r(array, 3, sizeof(int), cmp, &called);\n"
+ " return called && array[0] == 2 && array[1] == 5 && array[2] == 9 ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_STRUCT_TIMESPEC", "struct timespec declaration",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <time.h>\n"
+ "static void func(void) {\n"
+ " struct timespec ts;\n"
+ " ts.tv_sec = ts.tv_nsec = 1;\n"
+ "}\n" },
+ { "HAVE_SECTION_START_STOP", "__attribute__((section)) and __start/__stop",
+ "DEFINES_FUNC", NULL, NULL,
+ "static void *__attribute__((__section__(\"mysec\"))) p = &p;\n"
+ "static int func(void) {\n"
+ " extern void *__start_mysec[], *__stop_mysec[];\n"
+ " return __stop_mysec - __start_mysec;\n"
+ "}\n" },
+ { "HAVE_STACK_GROWS_UPWARDS", "stack grows upwards",
+ "DEFINES_EVERYTHING|EXECUTE", NULL, NULL,
+ "#include <stddef.h>\n"
+ "static ptrdiff_t nest(const void *base, unsigned int i)\n"
+ "{\n"
+ " if (i == 0)\n"
+ " return (const char *)&i - (const char *)base;\n"
+ " return nest(base, i-1);\n"
+ "}\n"
+ "int main(int argc, char *argv[]) {\n"
+ " (void)argv;\n"
+ " return (nest(&argc, argc) > 0) ? 0 : 1;\n"
+ "}\n" },
+ { "HAVE_STATEMENT_EXPR", "statement expression support",
+ "INSIDE_MAIN", NULL, NULL,
+ "return ({ int x = argc; x == argc ? 0 : 1; });" },
+ { "HAVE_SYS_FILIO_H", "<sys/filio.h>",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "#include <sys/filio.h>\n" },
+ { "HAVE_SYS_TERMIOS_H", "<sys/termios.h>",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "#include <sys/termios.h>\n" },
+ { "HAVE_SYS_UNISTD_H", "<sys/unistd.h>",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "#include <sys/unistd.h>\n" },
+ { "HAVE_UTIME", "utime() declaration",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <utime.h>\n"
+ "static int func(const char *filename) {\n"
+ " struct utimbuf times = { 0 };\n"
+ " return utime(filename, ×);\n"
+ "}" },
+ { "HAVE_WARN_UNUSED_RESULT", "__attribute__((warn_unused_result))",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <sys/types.h>\n"
+ "#include <utime.h>\n"
+ "static __attribute__((warn_unused_result)) int func(int i) {\n"
+ " return i + 1;\n"
+ "}" },
+ { "HAVE_OPENMP", "#pragma omp and -fopenmp support",
+ "INSIDE_MAIN|EXECUTE|MAY_NOT_COMPILE", NULL, NULL,
+ "int i;\n"
+ "#pragma omp parallel for\n"
+ "for(i = 0; i < 0; i++) {};\n"
+ "return 0;\n",
+ "-Werror -fopenmp" },
+ { "HAVE_VALGRIND_MEMCHECK_H", "<valgrind/memcheck.h>",
+ "OUTSIDE_MAIN", NULL, NULL,
+ "#include <valgrind/memcheck.h>\n" },
+ { "HAVE_UCONTEXT", "working <ucontext.h",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE",
+ NULL, NULL,
+ "#include <ucontext.h>\n"
+ "static int x = 0;\n"
+ "static char stack[2048];\n"
+ "static ucontext_t a, b;\n"
+ "static void fn(void) {\n"
+ " x |= 2;\n"
+ " setcontext(&b);\n"
+ " x |= 4;\n"
+ "}\n"
+ "int main(void) {\n"
+ " x |= 1;\n"
+ " getcontext(&a);\n"
+ " a.uc_stack.ss_sp = stack;\n"
+ " a.uc_stack.ss_size = sizeof(stack);\n"
+ " makecontext(&a, fn, 0);\n"
+ " swapcontext(&b, &a);\n"
+ " return (x == 3) ? 0 : 1;\n"
+ "}\n"
+ },
+ { "HAVE_POINTER_SAFE_MAKECONTEXT", "passing pointers via makecontext()",
+ "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE",
+ "HAVE_UCONTEXT", NULL,
+ "#include <stddef.h>\n"
+ "#include <ucontext.h>\n"
+ "static int worked = 0;\n"
+ "static char stack[1024];\n"
+ "static ucontext_t a, b;\n"
+ "static void fn(void *p, void *q) {\n"
+ " void *cp = &worked;\n"
+ " void *cq = (void *)(~((ptrdiff_t)cp));\n"
+ " if ((p == cp) && (q == cq))\n"
+ " worked = 1;\n"
+ " setcontext(&b);\n"
+ "}\n"
+ "int main(void) {\n"
+ " void *ap = &worked;\n"
+ " void *aq = (void *)(~((ptrdiff_t)ap));\n"
+ " getcontext(&a);\n"
+ " a.uc_stack.ss_sp = stack;\n"
+ " a.uc_stack.ss_size = sizeof(stack);\n"
+ " makecontext(&a, (void (*)(void))fn, 2, ap, aq);\n"
+ " swapcontext(&b, &a);\n"
+ " return worked ? 0 : 1;\n"
+ "}\n"
+ },
+ { "HAVE_BUILTIN_CPU_SUPPORTS", "__builtin_cpu_supports()",
+ "DEFINES_FUNC", NULL, NULL,
+ "#include <stdbool.h>\n"
+ "static bool func(void) {\n"
+ " return __builtin_cpu_supports(\"mmx\");\n"
+ "}"
+ },
+ { "HAVE_CLOSEFROM", "closefrom() offered by system",
+ "DEFINES_EVERYTHING", NULL, NULL,
+ "#include <stdlib.h>\n"
+ "#include <unistd.h>\n"
+ "int main(void) {\n"
+ " closefrom(STDERR_FILENO + 1);\n"
+ " return 0;\n"
+ "}\n"
+ },
+ { "HAVE_F_CLOSEM", "F_CLOSEM defined for fctnl.",
+ "DEFINES_EVERYTHING", NULL, NULL,
+ "#include <fcntl.h>\n"
+ "#include <unistd.h>\n"
+ "int main(void) {\n"
+ " int res = fcntl(STDERR_FILENO + 1, F_CLOSEM, 0);\n"
+ " return res < 0;\n"
+ "}\n"
+ },
+ { "HAVE_NR_CLOSE_RANGE", "close_range syscall available as __NR_close_range.",
+ "DEFINES_EVERYTHING", NULL, NULL,
+ "#include <limits.h>\n"
+ "#include <sys/syscall.h>\n"
+ "#include <unistd.h>\n"
+ "int main(void) {\n"
+ " int res = syscall(__NR_close_range, STDERR_FILENO + 1, INT_MAX, 0);\n"
+ " return res < 0;\n"
+ "}\n"
+ },
+ { "HAVE_F_MAXFD", "F_MAXFD defined for fcntl.",
+ "DEFINES_EVERYTHING", NULL, NULL,
+ "#include <fcntl.h>\n"
+ "#include <unistd.h>\n"
+ "int main(void) {\n"
+ " int res = fcntl(0, F_MAXFD);\n"
+ " return res < 0;\n"
+ "}\n"
+ },
+ */
+};
+
+static void c12r_err(int eval, const char *fmt, ...)
+{
+ int err_errno = errno;
+ va_list ap;
+
+ fprintf(stderr, "%s: ", progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, ": %s\n", strerror(err_errno));
+ exit(eval);
+}
+
+static void c12r_errx(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s: ", progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(eval);
+}
+
+static void start_test(const char *what, const char *why)
+{
+ if (like_a_libtool) {
+ printf("%s%s... ", what, why);
+ fflush(stdout);
+ }
+}
+
+static void end_test(bool result)
+{
+ if (like_a_libtool)
+ printf("%s\n", result ? "yes" : "no");
+}
+
+static size_t fcopy(FILE *fsrc, FILE *fdst)
+{
+ char buffer[BUFSIZ];
+ size_t rsize, wsize;
+ size_t copied = 0;
+
+ while ((rsize = fread(buffer, 1, BUFSIZ, fsrc)) > 0) {
+ wsize = fwrite(buffer, 1, rsize, fdst);
+ copied += wsize;
+ if (wsize != rsize)
+ break;
+ }
+
+ return copied;
+}
+
+static char *grab_stream(FILE *file)
+{
+ size_t max, ret, size = 0;
+ char *buffer;
+
+ max = BUFSIZ;
+ buffer = malloc(max);
+ while ((ret = fread(buffer+size, 1, max - size, file)) == max - size) {
+ size += ret;
+ buffer = realloc(buffer, max *= 2);
+ }
+ size += ret;
+ if (ferror(file))
+ c12r_err(EXIT_TROUBLE_RUNNING, "reading from command");
+ buffer[size] = '\0';
+ return buffer;
+}
+
+static char *run(const char *cmd, int *exitstatus)
+{
+ static const char redir[] = " 2>&1";
+ size_t cmdlen;
+ char *cmdredir;
+ FILE *cmdout;
+ char *ret;
+
+ cmdlen = strlen(cmd);
+ cmdredir = malloc(cmdlen + sizeof(redir));
+ memcpy(cmdredir, cmd, cmdlen);
+ memcpy(cmdredir + cmdlen, redir, sizeof(redir));
+
+ cmdout = popen(cmdredir, "r");
+ if (!cmdout)
+ c12r_err(EXIT_TROUBLE_RUNNING, "popen \"%s\"", cmdredir);
+
+ free(cmdredir);
+
+ ret = grab_stream(cmdout);
+ *exitstatus = pclose(cmdout);
+ return ret;
+}
+
+static char *connect_args(const char *argv[], const char *outflag,
+ const char *files)
+{
+ unsigned int i;
+ char *ret;
+ size_t len = strlen(outflag) + strlen(files) + 1;
+
+ for (i = 1; argv[i]; i++)
+ len += 1 + strlen(argv[i]);
+
+ ret = malloc(len);
+ len = 0;
+ for (i = 1; argv[i]; i++) {
+ strcpy(ret + len, argv[i]);
+ len += strlen(argv[i]);
+ if (argv[i+1] || *outflag)
+ ret[len++] = ' ';
+ }
+ strcpy(ret + len, outflag);
+ len += strlen(outflag);
+ strcpy(ret + len, files);
+ return ret;
+}
+
+static struct test *find_test(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; tests[i].name; i++) {
+ if (strcmp(tests[i].name, name) == 0)
+ return &tests[i];
+ }
+ c12r_errx(EXIT_BAD_TEST, "Unknown test %s", name);
+ abort();
+}
+
+#define PRE_BOILERPLATE "/* Test program generated by configurator. */\n"
+#define MAIN_START_BOILERPLATE \
+ "int main(int argc, char *argv[]) {\n" \
+ " (void)argc;\n" \
+ " (void)argv;\n"
+#define USE_FUNC_BOILERPLATE "(void)func;\n"
+#define MAIN_BODY_BOILERPLATE "return 0;\n"
+#define MAIN_END_BOILERPLATE "}\n"
+
+static bool run_test(const char *cmd, const char *wrapper, struct test *test)
+{
+ char *output, *newcmd;
+ FILE *outf;
+ int status;
+
+ if (test->done)
+ return test->answer;
+
+ if (test->depends) {
+ size_t len;
+ const char *deps = test->depends;
+ char *dep;
+
+ /* Space-separated dependencies, could be ! for inverse. */
+ while ((len = strcspn(deps, " ")) != 0) {
+ bool positive = true;
+ if (deps[len]) {
+ dep = strdup(deps);
+ dep[len] = '\0';
+ } else {
+ dep = (char *)deps;
+ }
+
+ if (dep[0] == '!') {
+ dep++;
+ positive = false;
+ }
+ if (run_test(cmd, wrapper, find_test(dep)) != positive) {
+ test->answer = false;
+ test->done = true;
+ return test->answer;
+ }
+ if (deps[len])
+ free(dep);
+
+ deps += len;
+ deps += strspn(deps, " ");
+ }
+ }
+
+ outf = fopen(INPUT_FILE, verbose > 1 ? "w+" : "w");
+ if (!outf)
+ c12r_err(EXIT_TROUBLE_RUNNING, "creating %s", INPUT_FILE);
+
+ fprintf(outf, "%s", PRE_BOILERPLATE);
+
+ if (strstr(test->style, "INSIDE_MAIN")) {
+ fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+ fprintf(outf, "%s", test->fragment);
+ fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+ } else if (strstr(test->style, "OUTSIDE_MAIN")) {
+ fprintf(outf, "%s", test->fragment);
+ fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_BODY_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+ } else if (strstr(test->style, "DEFINES_FUNC")) {
+ fprintf(outf, "%s", test->fragment);
+ fprintf(outf, "%s", MAIN_START_BOILERPLATE);
+ fprintf(outf, "%s", USE_FUNC_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_BODY_BOILERPLATE);
+ fprintf(outf, "%s", MAIN_END_BOILERPLATE);
+ } else if (strstr(test->style, "DEFINES_EVERYTHING")) {
+ fprintf(outf, "%s", test->fragment);
+ } else
+ c12r_errx(EXIT_BAD_TEST, "Unknown style for test %s: %s",
+ test->name, test->style);
+
+ if (verbose > 1) {
+ fseek(outf, 0, SEEK_SET);
+ fcopy(outf, stdout);
+ }
+
+ fclose(outf);
+
+ newcmd = strdup(cmd);
+
+ if (test->flags) {
+ newcmd = realloc(newcmd, strlen(newcmd) + strlen(" ")
+ + strlen(test->flags) + 1);
+ strcat(newcmd, " ");
+ strcat(newcmd, test->flags);
+ if (verbose > 1)
+ printf("Extra flags line: %s", newcmd);
+ }
+
+ if (test->link) {
+ newcmd = realloc(newcmd, strlen(newcmd) + strlen(" ")
+ + strlen(test->link) + 1);
+ strcat(newcmd, " ");
+ strcat(newcmd, test->link);
+ if (verbose > 1)
+ printf("Extra link line: %s", newcmd);
+ }
+
+ start_test("checking for ", test->desc);
+ output = run(newcmd, &status);
+
+ free(newcmd);
+
+ if (status != 0 || strstr(output, "warning")) {
+ if (verbose)
+ printf("Compile %s for %s, status %i: %s\n",
+ status ? "fail" : "warning",
+ test->name, status, output);
+ if (strstr(test->style, "EXECUTE")
+ && !strstr(test->style, "MAY_NOT_COMPILE"))
+ c12r_errx(EXIT_BAD_TEST,
+ "Test for %s did not compile:\n%s",
+ test->name, output);
+ test->answer = false;
+ free(output);
+ } else {
+ /* Compile succeeded. */
+ free(output);
+ /* We run INSIDE_MAIN tests for sanity checking. */
+ if (strstr(test->style, "EXECUTE")
+ || strstr(test->style, "INSIDE_MAIN")) {
+ char *cmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1);
+
+ strcpy(cmd, wrapper);
+ strcat(cmd, " ." DIR_SEP OUTPUT_FILE);
+ output = run(cmd, &status);
+ free(cmd);
+ if (!strstr(test->style, "EXECUTE") && status != 0)
+ c12r_errx(EXIT_BAD_TEST,
+ "Test for %s failed with %i:\n%s",
+ test->name, status, output);
+ if (verbose && status)
+ printf("%s exited %i\n", test->name, status);
+ free(output);
+ }
+ test->answer = (status == 0);
+ }
+ test->done = true;
+ end_test(test->answer);
+
+ if (test->answer && test->overrides) {
+ struct test *override = find_test(test->overrides);
+ override->done = true;
+ override->answer = true;
+ }
+ return test->answer;
+}
+
+static char *any_field(char **fieldname)
+{
+ char buf[1000];
+ for (;;) {
+ char *p, *eq;
+
+ if (!fgets(buf, sizeof(buf), stdin))
+ return NULL;
+
+ p = buf;
+ /* Ignore whitespace, lines starting with # */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '#' || *p == '\n')
+ continue;
+
+ eq = strchr(p, '=');
+ if (!eq)
+ c12r_errx(EXIT_BAD_INPUT, "no = in line: %s", p);
+ *eq = '\0';
+ *fieldname = strdup(p);
+ p = eq + 1;
+ if (strlen(p) && p[strlen(p)-1] == '\n')
+ p[strlen(p)-1] = '\0';
+ return strdup(p);
+ }
+}
+
+static char *read_field(const char *name, bool compulsory)
+{
+ char *fieldname, *value;
+
+ value = any_field(&fieldname);
+ if (!value) {
+ if (!compulsory)
+ return NULL;
+ c12r_errx(EXIT_BAD_INPUT, "Could not read field %s", name);
+ }
+ if (strcmp(fieldname, name) != 0)
+ c12r_errx(EXIT_BAD_INPUT,
+ "Expected field %s not %s", name, fieldname);
+ return value;
+}
+
+/* Test descriptions from stdin:
+ * Lines starting with # or whitespace-only are ignored.
+ *
+ * First three non-ignored lines must be:
+ * var=<varname>
+ * desc=<description-for-autotools-style>
+ * style=OUTSIDE_MAIN DEFINES_FUNC INSIDE_MAIN DEFINES_EVERYTHING EXECUTE MAY_NOT_COMPILE
+ *
+ * Followed by optional lines:
+ * depends=<space-separated-testnames, ! to invert>
+ * link=<extra args for link line>
+ * flags=<extra args for compile line>
+ * overrides=<testname-to-force>
+ *
+ * Finally a code line, either:
+ * code=<oneline> OR
+ * code=
+ * <lines of code>
+ * <end-comment>
+ *
+ * And <end-comment> looks like this next comment: */
+/*END*/
+static bool read_test(struct test *test)
+{
+ char *field, *value;
+ char buf[1000];
+
+ memset(test, 0, sizeof(*test));
+ test->name = read_field("var", false);
+ if (!test->name)
+ return false;
+ test->desc = read_field("desc", true);
+ test->style = read_field("style", true);
+ /* Read any optional fields. */
+ while ((value = any_field(&field)) != NULL) {
+ if (strcmp(field, "depends") == 0)
+ test->depends = value;
+ else if (strcmp(field, "link") == 0)
+ test->link = value;
+ else if (strcmp(field, "flags") == 0)
+ test->flags = value;
+ else if (strcmp(field, "overrides") == 0)
+ test->overrides = value;
+ else if (strcmp(field, "code") == 0)
+ break;
+ else
+ c12r_errx(EXIT_BAD_INPUT, "Unknown field %s in %s",
+ field, test->name);
+ }
+ if (!value)
+ c12r_errx(EXIT_BAD_INPUT, "Missing code in %s", test->name);
+
+ if (strlen(value) == 0) {
+ /* Multiline program, read to END comment */
+ while (fgets(buf, sizeof(buf), stdin) != 0) {
+ size_t n;
+ if (strncmp(buf, "/*END*/", 7) == 0)
+ break;
+ n = strlen(value);
+ value = realloc(value, n + strlen(buf) + 1);
+ strcpy(value + n, buf);
+ n += strlen(buf);
+ }
+ }
+ test->fragment = value;
+ return true;
+}
+
+static void read_tests(size_t num_tests)
+{
+ while (read_test(tests + num_tests)) {
+ num_tests++;
+ tests = realloc(tests, (num_tests + 1) * sizeof(tests[0]));
+ tests[num_tests].name = NULL;
+ }
+}
+
+int main(int argc, const char *argv[])
+{
+ char *cmd;
+ unsigned int i;
+ const char *default_args[]
+ = { "", DEFAULT_COMPILER, DEFAULT_FLAGS, NULL };
+ const char *outflag = DEFAULT_OUTPUT_EXE_FLAG;
+ const char *configurator_cc = NULL;
+ const char *wrapper = "";
+ const char *orig_cc;
+ const char *varfile = NULL;
+ const char *headerfile = NULL;
+ bool extra_tests = false;
+ FILE *outf;
+
+ if (argc > 0)
+ progname = argv[0];
+
+ while (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0) {
+ printf("Usage: configurator [-v] [--var-file=<filename>] [-O<outflag>] [--configurator-cc=<compiler-for-tests>] [--wrapper=<wrapper-for-tests>] [--autotools-style] [--extra-tests] [<compiler> <flags>...]\n"
+ " <compiler> <flags> will have \"<outflag> <outfile> <infile.c>\" appended\n"
+ "Default: %s %s %s\n",
+ DEFAULT_COMPILER, DEFAULT_FLAGS,
+ DEFAULT_OUTPUT_EXE_FLAG);
+ exit(0);
+ }
+ if (strncmp(argv[1], "-O", 2) == 0) {
+ argc--;
+ argv++;
+ outflag = argv[1] + 2;
+ if (!*outflag) {
+ fprintf(stderr,
+ "%s: option requires an argument -- O\n",
+ argv[0]);
+ exit(EXIT_BAD_USAGE);
+ }
+ } else if (strcmp(argv[1], "-v") == 0) {
+ argc--;
+ argv++;
+ verbose++;
+ } else if (strcmp(argv[1], "-vv") == 0) {
+ argc--;
+ argv++;
+ verbose += 2;
+ } else if (strncmp(argv[1], "--configurator-cc=", 18) == 0) {
+ configurator_cc = argv[1] + 18;
+ argc--;
+ argv++;
+ } else if (strncmp(argv[1], "--wrapper=", 10) == 0) {
+ wrapper = argv[1] + 10;
+ argc--;
+ argv++;
+ } else if (strncmp(argv[1], "--var-file=", 11) == 0) {
+ varfile = argv[1] + 11;
+ argc--;
+ argv++;
+ } else if (strcmp(argv[1], "--autotools-style") == 0) {
+ like_a_libtool = true;
+ argc--;
+ argv++;
+ } else if (strncmp(argv[1], "--header-file=", 14) == 0) {
+ headerfile = argv[1] + 14;
+ argc--;
+ argv++;
+ } else if (strcmp(argv[1], "--extra-tests") == 0) {
+ extra_tests = true;
+ argc--;
+ argv++;
+ } else if (strcmp(argv[1], "--") == 0) {
+ break;
+ } else if (argv[1][0] == '-') {
+ c12r_errx(EXIT_BAD_USAGE, "Unknown option %s", argv[1]);
+ } else {
+ break;
+ }
+ }
+
+ if (argc == 1)
+ argv = default_args;
+
+ /* Copy with NULL entry at end */
+ tests = calloc(sizeof(base_tests)/sizeof(base_tests[0]) + 1,
+ sizeof(base_tests[0]));
+ memcpy(tests, base_tests, sizeof(base_tests));
+
+ if (extra_tests)
+ read_tests(sizeof(base_tests)/sizeof(base_tests[0]));
+
+ orig_cc = argv[1];
+ if (configurator_cc)
+ argv[1] = configurator_cc;
+
+ cmd = connect_args(argv, outflag, OUTPUT_FILE " " INPUT_FILE);
+ if (like_a_libtool) {
+ start_test("Making autoconf users comfortable", "");
+ sleep(1);
+ end_test(1);
+ }
+ for (i = 0; tests[i].name; i++)
+ run_test(cmd, wrapper, &tests[i]);
+ free(cmd);
+
+ remove(OUTPUT_FILE);
+ remove(INPUT_FILE);
+
+ if (varfile) {
+ FILE *vars;
+
+ if (strcmp(varfile, "-") == 0)
+ vars = stdout;
+ else {
+ start_test("Writing variables to ", varfile);
+ vars = fopen(varfile, "a");
+ if (!vars)
+ c12r_err(EXIT_TROUBLE_RUNNING,
+ "Could not open %s", varfile);
+ }
+ for (i = 0; tests[i].name; i++)
+ fprintf(vars, "%s=%u\n", tests[i].name, tests[i].answer);
+ if (vars != stdout) {
+ if (fclose(vars) != 0)
+ c12r_err(EXIT_TROUBLE_RUNNING,
+ "Closing %s", varfile);
+ end_test(1);
+ }
+ }
+
+ if (headerfile) {
+ start_test("Writing header to ", headerfile);
+ outf = fopen(headerfile, "w");
+ if (!outf)
+ c12r_err(EXIT_TROUBLE_RUNNING,
+ "Could not open %s", headerfile);
+ } else
+ outf = stdout;
+
+ fprintf(outf, "/* Generated by CCAN configurator */\n"
+ "#ifndef CCAN_CONFIG_H\n"
+ "#define CCAN_CONFIG_H\n");
+ fprintf(outf, "#ifndef _GNU_SOURCE\n");
+ fprintf(outf, "#define _GNU_SOURCE /* Always use GNU extensions. */\n");
+ fprintf(outf, "#endif\n");
+ fprintf(outf, "#define CCAN_COMPILER \"%s\"\n", orig_cc);
+ cmd = connect_args(argv + 1, "", "");
+ fprintf(outf, "#define CCAN_CFLAGS \"%s\"\n", cmd);
+ free(cmd);
+ fprintf(outf, "#define CCAN_OUTPUT_EXE_CFLAG \"%s\"\n\n", outflag);
+ /* This one implies "#include <ccan/..." works, eg. for tdb2.h */
+ fprintf(outf, "#define HAVE_CCAN 1\n");
+ for (i = 0; tests[i].name; i++)
+ fprintf(outf, "#define %s %u\n", tests[i].name, tests[i].answer);
+ fprintf(outf, "#endif /* CCAN_CONFIG_H */\n");
+
+ if (headerfile) {
+ if (fclose(outf) != 0)
+ c12r_err(EXIT_TROUBLE_RUNNING, "Closing %s", headerfile);
+ end_test(1);
+ }
+
+ return 0;
+}
diff --git a/nostrdb/endian.h b/nostrdb/endian.h
@@ -1,8 +1,10 @@
-/* CC0 (Public domain) - see LICENSE file for details */
+/* CC0 (Public domain) */
#ifndef CCAN_ENDIAN_H
#define CCAN_ENDIAN_H
#include <stdint.h>
+
#include "config.h"
+#include "cursor.h"
/**
* BSWAP_16 - reverse bytes in a constant uint16_t value.
@@ -11,13 +13,13 @@
* Designed to be usable in constant-requiring initializers.
*
* Example:
- * struct mystruct {
- * char buf[BSWAP_16(0x1234)];
- * };
+ * struct mystruct {
+ * char buf[BSWAP_16(0x1234)];
+ * };
*/
-#define BSWAP_16(val) \
- ((((uint16_t)(val) & 0x00ff) << 8) \
- | (((uint16_t)(val) & 0xff00) >> 8))
+#define BSWAP_16(val) \
+ ((((uint16_t)(val) & 0x00ff) << 8) \
+ | (((uint16_t)(val) & 0xff00) >> 8))
/**
* BSWAP_32 - reverse bytes in a constant uint32_t value.
@@ -26,15 +28,15 @@
* Designed to be usable in constant-requiring initializers.
*
* Example:
- * struct mystruct {
- * char buf[BSWAP_32(0xff000000)];
- * };
+ * struct mystruct {
+ * char buf[BSWAP_32(0xff000000)];
+ * };
*/
-#define BSWAP_32(val) \
- ((((uint32_t)(val) & 0x000000ff) << 24) \
- | (((uint32_t)(val) & 0x0000ff00) << 8) \
- | (((uint32_t)(val) & 0x00ff0000) >> 8) \
- | (((uint32_t)(val) & 0xff000000) >> 24))
+#define BSWAP_32(val) \
+ ((((uint32_t)(val) & 0x000000ff) << 24) \
+ | (((uint32_t)(val) & 0x0000ff00) << 8) \
+ | (((uint32_t)(val) & 0x00ff0000) >> 8) \
+ | (((uint32_t)(val) & 0xff000000) >> 24))
/**
* BSWAP_64 - reverse bytes in a constant uint64_t value.
@@ -43,19 +45,19 @@
* Designed to be usable in constant-requiring initializers.
*
* Example:
- * struct mystruct {
- * char buf[BSWAP_64(0xff00000000000000ULL)];
- * };
- */
-#define BSWAP_64(val) \
- ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \
- | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \
- | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \
- | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \
- | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \
- | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \
- | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \
- | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56))
+ * struct mystruct {
+ * char buf[BSWAP_64(0xff00000000000000ULL)];
+ * };
+ */
+#define BSWAP_64(val) \
+ ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \
+ | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \
+ | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \
+ | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \
+ | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \
+ | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \
+ | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \
+ | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56))
#if HAVE_BYTESWAP_H
#include <byteswap.h>
@@ -65,12 +67,12 @@
* @val: value whose bytes to swap.
*
* Example:
- * // Output contains "1024 is 4 as two bytes reversed"
- * printf("1024 is %u as two bytes reversed\n", bswap_16(1024));
+ * // Output contains "1024 is 4 as two bytes reversed"
+ * printf("1024 is %u as two bytes reversed\n", bswap_16(1024));
*/
static inline uint16_t bswap_16(uint16_t val)
{
- return BSWAP_16(val);
+ return BSWAP_16(val);
}
/**
@@ -78,12 +80,12 @@ static inline uint16_t bswap_16(uint16_t val)
* @val: value whose bytes to swap.
*
* Example:
- * // Output contains "1024 is 262144 as four bytes reversed"
- * printf("1024 is %u as four bytes reversed\n", bswap_32(1024));
+ * // Output contains "1024 is 262144 as four bytes reversed"
+ * printf("1024 is %u as four bytes reversed\n", bswap_32(1024));
*/
static inline uint32_t bswap_32(uint32_t val)
{
- return BSWAP_32(val);
+ return BSWAP_32(val);
}
#endif /* !HAVE_BYTESWAP_H */
@@ -93,19 +95,19 @@ static inline uint32_t bswap_32(uint32_t val)
* @val: value whose bytes to swap.
*
* Example:
- * // Output contains "1024 is 1125899906842624 as eight bytes reversed"
- * printf("1024 is %llu as eight bytes reversed\n",
- * (unsigned long long)bswap_64(1024));
+ * // Output contains "1024 is 1125899906842624 as eight bytes reversed"
+ * printf("1024 is %llu as eight bytes reversed\n",
+ * (unsigned long long)bswap_64(1024));
*/
static inline uint64_t bswap_64(uint64_t val)
{
- return BSWAP_64(val);
+ return BSWAP_64(val);
}
#endif
/* Needed for Glibc like endiness check */
-#define __LITTLE_ENDIAN 1234
-#define __BIG_ENDIAN 4321
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
/* Sanity check the defines. We don't handle weird endianness. */
#if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN
@@ -114,13 +116,13 @@ static inline uint64_t bswap_64(uint64_t val)
#error "Can't compile for both big and little endian."
#elif HAVE_LITTLE_ENDIAN
#ifndef __BYTE_ORDER
-#define __BYTE_ORDER __LITTLE_ENDIAN
+#define __BYTE_ORDER __LITTLE_ENDIAN
#elif __BYTE_ORDER != __LITTLE_ENDIAN
#error "__BYTE_ORDER already defined, but not equal to __LITTLE_ENDIAN"
#endif
#elif HAVE_BIG_ENDIAN
#ifndef __BYTE_ORDER
-#define __BYTE_ORDER __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
#elif __BYTE_ORDER != __BIG_ENDIAN
#error "__BYTE_ORDER already defined, but not equal to __BIG_ENDIAN"
#endif
@@ -242,7 +244,7 @@ typedef uint16_t ENDIAN_TYPE beint16_t;
*/
static inline leint64_t cpu_to_le64(uint64_t native)
{
- return CPU_TO_LE64(native);
+ return CPU_TO_LE64(native);
}
/**
@@ -251,7 +253,7 @@ static inline leint64_t cpu_to_le64(uint64_t native)
*/
static inline leint32_t cpu_to_le32(uint32_t native)
{
- return CPU_TO_LE32(native);
+ return CPU_TO_LE32(native);
}
/**
@@ -260,7 +262,7 @@ static inline leint32_t cpu_to_le32(uint32_t native)
*/
static inline leint16_t cpu_to_le16(uint16_t native)
{
- return CPU_TO_LE16(native);
+ return CPU_TO_LE16(native);
}
/**
@@ -269,7 +271,7 @@ static inline leint16_t cpu_to_le16(uint16_t native)
*/
static inline uint64_t le64_to_cpu(leint64_t le_val)
{
- return LE64_TO_CPU(le_val);
+ return LE64_TO_CPU(le_val);
}
/**
@@ -278,7 +280,7 @@ static inline uint64_t le64_to_cpu(leint64_t le_val)
*/
static inline uint32_t le32_to_cpu(leint32_t le_val)
{
- return LE32_TO_CPU(le_val);
+ return LE32_TO_CPU(le_val);
}
/**
@@ -287,7 +289,7 @@ static inline uint32_t le32_to_cpu(leint32_t le_val)
*/
static inline uint16_t le16_to_cpu(leint16_t le_val)
{
- return LE16_TO_CPU(le_val);
+ return LE16_TO_CPU(le_val);
}
/**
@@ -296,7 +298,7 @@ static inline uint16_t le16_to_cpu(leint16_t le_val)
*/
static inline beint64_t cpu_to_be64(uint64_t native)
{
- return CPU_TO_BE64(native);
+ return CPU_TO_BE64(native);
}
/**
@@ -305,7 +307,7 @@ static inline beint64_t cpu_to_be64(uint64_t native)
*/
static inline beint32_t cpu_to_be32(uint32_t native)
{
- return CPU_TO_BE32(native);
+ return CPU_TO_BE32(native);
}
/**
@@ -314,7 +316,7 @@ static inline beint32_t cpu_to_be32(uint32_t native)
*/
static inline beint16_t cpu_to_be16(uint16_t native)
{
- return CPU_TO_BE16(native);
+ return CPU_TO_BE16(native);
}
/**
@@ -323,7 +325,7 @@ static inline beint16_t cpu_to_be16(uint16_t native)
*/
static inline uint64_t be64_to_cpu(beint64_t be_val)
{
- return BE64_TO_CPU(be_val);
+ return BE64_TO_CPU(be_val);
}
/**
@@ -332,7 +334,7 @@ static inline uint64_t be64_to_cpu(beint64_t be_val)
*/
static inline uint32_t be32_to_cpu(beint32_t be_val)
{
- return BE32_TO_CPU(be_val);
+ return BE32_TO_CPU(be_val);
}
/**
@@ -341,11 +343,9 @@ static inline uint32_t be32_to_cpu(beint32_t be_val)
*/
static inline uint16_t be16_to_cpu(beint16_t be_val)
{
- return BE16_TO_CPU(be_val);
+ return BE16_TO_CPU(be_val);
}
-/* Whichever they include first, they get these definitions. */
-#ifdef CCAN_SHORT_TYPES_H
/**
* be64/be32/be16 - 64/32/16 bit big-endian representation.
*/
@@ -359,5 +359,7 @@ typedef beint16_t be16;
typedef leint64_t le64;
typedef leint32_t le32;
typedef leint16_t le16;
-#endif
+
+
#endif /* CCAN_ENDIAN_H */
+
diff --git a/nostrdb/hex.h b/nostrdb/hex.h
@@ -1,84 +1,68 @@
-/* CC0 (Public domain) - see LICENSE file for details */
-#ifndef CCAN_HEX_H
-#define CCAN_HEX_H
-#include "config.h"
-#include <stdbool.h>
-#include <stdlib.h>
-/**
- * hex_decode - Unpack a hex string.
- * @str: the hexadecimal string
- * @slen: the length of @str
- * @buf: the buffer to write the data into
- * @bufsize: the length of
- *
- * Returns false if there are any characters which aren't 0-9, a-f or A-F,
- * of the string wasn't the right length for @bufsize.
- *
- * Example:
- * unsigned char data[20];
- *
- * if (!hex_decode(argv[1], strlen(argv[1]), data, 20))
- * printf("String is malformed!\n");
- */
-bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize);
+#ifndef HEX_H
+#define HEX_H
+
+#include <stdlib.h>
-/**
- * hex_encode - Create a nul-terminated hex string
- * @buf: the buffer to read the data from
- * @bufsize: the length of buf
- * @dest: the string to fill
- * @destsize: the max size of the string
- *
- * Returns true if the string, including terminator, fit in @destsize;
- *
- * Example:
- * unsigned char buf[] = { 0x1F, 0x2F };
- * char str[5];
- *
- * if (!hex_encode(buf, sizeof(buf), str, sizeof(str)))
- * abort();
- */
-bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize);
+static const char hex_table[256] = {
+ ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3,
+ ['4'] = 4, ['5'] = 5, ['6'] = 6, ['7'] = 7,
+ ['8'] = 8, ['9'] = 9, ['a'] = 10, ['b'] = 11,
+ ['c'] = 12, ['d'] = 13, ['e'] = 14, ['f'] = 15,
+ ['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13,
+ ['E'] = 14, ['F'] = 15
+};
-/**
- * hex_str_size - Calculate how big a nul-terminated hex string is
- * @bytes: bytes of data to represent
- *
- * Example:
- * unsigned char buf[] = { 0x1F, 0x2F };
- * char str[hex_str_size(sizeof(buf))];
- *
- * hex_encode(buf, sizeof(buf), str, sizeof(str));
- */
-static inline size_t hex_str_size(size_t bytes)
+static inline int char_to_hex(unsigned char *val, unsigned char c)
{
- return 2 * bytes + 1;
+ if (hex_table[c] || c == '0') {
+ *val = hex_table[c];
+ return 1;
+ }
+ return 0;
}
-/**
- * hex_data_size - Calculate how many bytes of data in a hex string
- * @strlen: the length of the string (with or without NUL)
- *
- * Example:
- * const char str[] = "1F2F";
- * unsigned char buf[hex_data_size(sizeof(str))];
- *
- * hex_decode(str, strlen(str), buf, sizeof(buf));
- */
-static inline size_t hex_data_size(size_t strlen)
+static inline int hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
{
- return strlen / 2;
+ unsigned char v1, v2;
+ unsigned char *p = buf;
+
+ while (slen > 1) {
+ if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1]))
+ return 0;
+ if (!bufsize)
+ return 0;
+ *(p++) = (v1 << 4) | v2;
+ str += 2;
+ slen -= 2;
+ bufsize--;
+ }
+ return slen == 0 && bufsize == 0;
}
+
static inline char hexchar(unsigned int val)
{
- if (val < 10)
- return '0' + val;
- if (val < 16)
- return 'a' + val - 10;
- abort();
+ if (val < 10)
+ return '0' + val;
+ if (val < 16)
+ return 'a' + val - 10;
+ abort();
+}
+
+static int hex_encode(const void *buf, size_t bufsize, char *dest)
+{
+ size_t i;
+
+ for (i = 0; i < bufsize; i++) {
+ unsigned int c = ((const unsigned char *)buf)[i];
+ *(dest++) = hexchar(c >> 4);
+ *(dest++) = hexchar(c & 0xF);
+ }
+ *dest = '\0';
+
+ return 1;
}
-#endif /* CCAN_HEX_H */
+#endif
diff --git a/nostrdb/io.h b/nostrdb/io.h
@@ -0,0 +1,48 @@
+
+#include <stdio.h>
+
+static int read_fd(FILE *fd, unsigned char *buf, int buflen, int *written)
+{
+ unsigned char *p = buf;
+ int len = 0;
+ *written = 0;
+
+ do {
+ len = fread(p, 1, 4096, fd);
+ *written += len;
+ p += len;
+ if (p > buf + buflen)
+ return 0;
+ } while (len == 4096);
+
+ return 1;
+}
+
+static int write_file(const char *filename, unsigned char *buf, int buflen)
+{
+ FILE *file = NULL;
+ int ok;
+
+ file = fopen(filename, "w");
+ if (file == NULL)
+ return 0;
+
+ ok = fwrite(buf, buflen, 1, file);
+ fclose(file);
+ return ok;
+}
+
+static int read_file(const char *filename, unsigned char *buf, int buflen, int *written)
+{
+ FILE *file = NULL;
+ int ok;
+
+ file = fopen(filename, "r");
+ if (file == NULL)
+ return 1;
+
+ ok = read_fd(file, buf, buflen, written);
+ fclose(file);
+ return ok;
+}
+
diff --git a/nostrdb/lmdb_util.h b/nostrdb/lmdb_util.h
@@ -0,0 +1,100 @@
+
+// Define callback function type
+typedef bool (*lmdb_callback_t)(const MDB_val*, const MDB_val*);
+
+
+int lmdb_foreach(MDB_txn *txn, MDB_dbi dbi, MDB_val *start, MDB_val *start_dup, callback_t cb, bool reverse) {
+ int success = 0;
+ MDB_cursor *cursor;
+
+ // Open a cursor on the provided transaction and database
+ int rc = mdb_cursor_open(txn, dbi, &cursor);
+
+ if (rc != 0)
+ return 0;
+
+ MDB_val k = *start, v = *start_dup;
+ MDB_cursor_op op = reverse ? MDB_PREV : MDB_NEXT;
+
+ // If we're scanning in reverse...
+ if (reverse) {
+ // Try to position the cursor at the first key-value pair where
+ // both the key and the value are greater than or equal to our
+ // starting point.
+ rc = mdb_cursor_get(cursor, &k, &v, MDB_GET_BOTH_RANGE);
+ if (rc == 0) {
+ if (v.mv_size != start_dup->mv_size ||
+ memcmp(v.mv_data, start_dup->mv_data, v.mv_size) != 0) {
+ // If the value doesn't match our starting
+ // point, step back to the previous record.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_PREV) != 0)
+ goto cleanup;
+ }
+ } else {
+ // If we couldn't find a record that matches both our
+ // starting key and value, try to find a record that
+ // matches just our starting key.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_SET) == 0) {
+ // If we find a match, move to the last value
+ // for this key, since we're scanning in
+ // reverse.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_LAST_DUP) != 0)
+ goto cleanup;
+ } else {
+ // If we can't find a record with our starting
+ // key, try to find the first record with a key
+ // greater than our starting key.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_SET_RANGE) == 0) {
+ // If we find such a record, step back
+ // to the previous record.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_PREV) != 0)
+ goto cleanup;
+ } else {
+ // If we can't even find a record with
+ // a key greater than our starting key,
+ // fall back to starting from the last
+ // record in the database.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_LAST) != 0)
+ goto cleanup;
+ }
+ }
+ }
+ // If we're not scanning in reverse...
+ else {
+ // Try to position the cursor at the first key-value
+ // pair where both the key and the value are greater
+ // than or equal to our starting point.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_SET) != 0) {
+ // If we couldn't find a record that matches
+ // both our starting key and value, try to find
+ // a record that matches just our starting key.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_SET_RANGE) != 0)
+ goto cleanup;
+
+ // If we can't find a record with our starting
+ // key, try to find the first record with a key
+ // greater than our starting key.
+ if (mdb_cursor_get(cursor, &k, &v, MDB_FIRST_DUP) != 0)
+ goto cleanup;
+ }
+ }
+ }
+
+ // Whether we're scanning forward or backward, start the actual
+ // iteration, moving one step at a time in the appropriate direction
+ // and calling the provided callback for each record.
+ do {
+ if (!cb(&k, &v))
+ goto cleanup;
+ } while (mdb_cursor_get(cursor, &k, &v, op) == 0);
+
+ // If we make it through the entire iteration without the callback
+ // returning false, return true to signal success.
+ success = 1;
+
+cleanup:
+ mdb_cursor_close(cursor);
+ return success;
+}
+
+
diff --git a/nostrdb/ndb.c b/nostrdb/ndb.c
@@ -0,0 +1,185 @@
+
+
+#include "nostrdb.h"
+#include "print_util.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int usage()
+{
+ printf("usage: ndb [--skip-verification] [-d db_dir] <command>\n\n");
+ printf("commands\n\n");
+ printf(" stat\n");
+ printf(" search [--oldest-first] [--limit 42] <fulltext query>\n");
+ printf(" import <line-delimited json file>\n\n");
+ printf("settings\n\n");
+ printf(" --skip-verification skip signature validation\n");
+ printf(" -d <db_dir> set database directory\n");
+ return 1;
+}
+
+
+static int map_file(const char *filename, unsigned char **p, size_t *flen)
+{
+ struct stat st;
+ int des;
+ stat(filename, &st);
+ *flen = st.st_size;
+
+ des = open(filename, O_RDONLY);
+
+ *p = mmap(NULL, *flen, PROT_READ, MAP_PRIVATE, des, 0);
+ close(des);
+
+ return *p != MAP_FAILED;
+}
+
+static inline void print_stat_counts(struct ndb_stat_counts *counts)
+{
+ printf("%zu\t%zu\t%zu\t%zu\n",
+ counts->count,
+ counts->key_size,
+ counts->value_size,
+ counts->key_size + counts->value_size);
+}
+
+static void print_stats(struct ndb_stat *stat)
+{
+ int i;
+ const char *name;
+ struct ndb_stat_counts *c;
+
+ struct ndb_stat_counts total;
+ ndb_stat_counts_init(&total);
+
+ printf("name\tcount\tkey_bytes\tvalue_bytes\ttotal_bytes\n");
+ printf("---\ndbs\n---\n");
+ for (i = 0; i < NDB_DBS; i++) {
+ name = ndb_db_name(i);
+
+ total.count += stat->dbs[i].count;
+ total.key_size += stat->dbs[i].key_size;
+ total.value_size += stat->dbs[i].value_size;
+
+ printf("%s\t", name);
+ print_stat_counts(&stat->dbs[i]);
+ }
+
+ printf("total\t");
+ print_stat_counts(&total);
+
+ printf("-----\nkinds\n-----\n");
+ for (i = 0; i < NDB_CKIND_COUNT; i++) {
+ c = &stat->common_kinds[i];
+ if (c->count == 0)
+ continue;
+
+ printf("%s\t", ndb_kind_name(i));
+ print_stat_counts(c);
+ }
+
+ if (stat->other_kinds.count != 0) {
+ printf("other\t");
+ print_stat_counts(&stat->other_kinds);
+ }
+}
+
+int ndb_print_search_keys(struct ndb_txn *txn);
+
+int main(int argc, char *argv[])
+{
+ struct ndb *ndb;
+ int i, flags, limit;
+ struct ndb_stat stat;
+ struct ndb_txn txn;
+ struct ndb_text_search_results results;
+ struct ndb_text_search_result *result;
+ const char *dir;
+ unsigned char *data;
+ size_t data_len;
+ struct ndb_config config;
+ struct ndb_text_search_config search_config;
+ ndb_default_config(&config);
+ ndb_default_text_search_config(&search_config);
+ ndb_config_set_mapsize(&config, 1024ULL * 1024ULL * 1024ULL * 1024ULL /* 1 TiB */);
+
+ if (argc < 2) {
+ return usage();
+ }
+
+ dir = ".";
+ flags = 0;
+ for (i = 0; i < 2; i++)
+ {
+ if (!strcmp(argv[1], "-d") && argv[2]) {
+ dir = argv[2];
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[1], "--skip-verification")) {
+ flags = NDB_FLAG_SKIP_NOTE_VERIFY;
+ argv += 1;
+ argc -= 1;
+ }
+ }
+
+ ndb_config_set_flags(&config, flags);
+
+ fprintf(stderr, "using db '%s'\n", dir);
+
+ if (!ndb_init(&ndb, dir, &config)) {
+ return 2;
+ }
+
+ if (argc >= 3 && !strcmp(argv[1], "search")) {
+ for (i = 0; i < 2; i++) {
+ if (!strcmp(argv[2], "--oldest-first")) {
+ ndb_text_search_config_set_order(&search_config, NDB_ORDER_ASCENDING);
+ argv++;
+ argc--;
+ } else if (!strcmp(argv[2], "--limit")) {
+ limit = atoi(argv[3]);
+ ndb_text_search_config_set_limit(&search_config, limit);
+ argv += 2;
+ argc -= 2;
+ }
+ }
+
+ ndb_begin_query(ndb, &txn);
+ ndb_text_search(&txn, argv[2], &results, &search_config);
+
+ // print results for now
+ for (i = 0; i < results.num_results; i++) {
+ result = &results.results[i];
+ printf("[%02d] ", i+1);
+ ndb_print_text_search_result(&txn, result);
+ }
+
+ ndb_end_query(&txn);
+ } else if (argc == 2 && !strcmp(argv[1], "stat")) {
+ if (!ndb_stat(ndb, &stat)) {
+ return 3;
+ }
+
+ print_stats(&stat);
+ } else if (argc == 3 && !strcmp(argv[1], "import")) {
+ if (!strcmp(argv[2], "-")) {
+ ndb_process_events_stream(ndb, stdin);
+ } else {
+ map_file(argv[2], &data, &data_len);
+ ndb_process_events(ndb, (const char *)data, data_len);
+ ndb_process_client_events(ndb, (const char *)data, data_len);
+ }
+ } else if (argc == 2 && !strcmp(argv[1], "print-search-keys")) {
+ ndb_begin_query(ndb, &txn);
+ ndb_print_search_keys(&txn);
+ ndb_end_query(&txn);
+ } else {
+ return usage();
+ }
+
+ ndb_destroy(ndb);
+}
diff --git a/nostrdb/nostr_bech32.c b/nostrdb/nostr_bech32.c
@@ -7,10 +7,8 @@
#include "nostr_bech32.h"
#include <stdlib.h>
-#include "endian.h"
#include "cursor.h"
#include "bech32.h"
-#include <stdbool.h>
#define MAX_TLVS 16
@@ -147,11 +145,6 @@ static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
return 1;
}
-static uint32_t decode_tlv_u32(const uint8_t *bytes) {
- beint32_t *be32_bytes = (beint32_t*)bytes;
- return be32_to_cpu(*be32_bytes);
-}
-
static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *nevent) {
struct nostr_tlvs tlvs;
struct nostr_tlv *tlv;
@@ -173,13 +166,6 @@ static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *n
nevent->pubkey = NULL;
}
- if(find_tlv(&tlvs, TLV_KIND, &tlv)) {
- nevent->kind = decode_tlv_u32(tlv->value);
- nevent->has_kind = true;
- } else {
- nevent->has_kind = false;
- }
-
return tlvs_to_relays(&tlvs, &nevent->relays);
}
@@ -201,11 +187,6 @@ static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *nad
naddr->pubkey = tlv->value;
- if(!find_tlv(&tlvs, TLV_KIND, &tlv)) {
- return 0;
- }
- naddr->kind = decode_tlv_u32(tlv->value);
-
return tlvs_to_relays(&tlvs, &naddr->relays);
}
diff --git a/nostrdb/nostr_bech32.h b/nostrdb/nostr_bech32.h
@@ -11,8 +11,6 @@
#include <stdio.h>
#include "str_block.h"
#include "cursor.h"
-#include <stdbool.h>
-
typedef unsigned char u8;
#define MAX_RELAYS 10
@@ -47,8 +45,6 @@ struct bech32_nevent {
struct relays relays;
const u8 *event_id;
const u8 *pubkey; // optional
- uint32_t kind;
- bool has_kind;
};
struct bech32_nprofile {
@@ -60,7 +56,6 @@ struct bech32_naddr {
struct relays relays;
struct str_block identifier;
const u8 *pubkey;
- uint32_t kind;
};
struct bech32_nrelay {
diff --git a/nostrdb/nostrdb.c b/nostrdb/nostrdb.c
@@ -912,9 +912,9 @@ static int _ndb_begin_query(struct ndb *ndb, struct ndb_txn *txn, int flags)
{
txn->lmdb = &ndb->lmdb;
MDB_txn **mdb_txn = (MDB_txn **)&txn->mdb_txn;
- if (!txn->lmdb->env)
- return 0;
- return mdb_txn_begin(txn->lmdb->env, NULL, flags, mdb_txn) == 0;
+ if (!txn->lmdb->env)
+ return 0;
+ return mdb_txn_begin(txn->lmdb->env, NULL, flags, mdb_txn) == 0;
}
int ndb_begin_query(struct ndb *ndb, struct ndb_txn *txn)
@@ -2163,15 +2163,6 @@ static int ndb_write_note_kind_index(struct ndb_txn *txn, struct ndb_note *note,
return 1;
}
-static void consume_whitespace_or_punctuation(struct cursor *cur)
-{
- while (cur->p < cur->end) {
- if (!is_right_boundary(*cur->p))
- return;
- cur->p++;
- }
-}
-
static int ndb_write_word_to_index(struct ndb_txn *txn, const char *word,
int word_len, int word_index,
uint64_t timestamp, uint64_t note_id)
@@ -3570,7 +3561,7 @@ static int ndb_event_commitment(struct ndb_note *ev, unsigned char *buf, int buf
struct cursor cur;
int ok;
- if (!hex_encode(ev->pubkey, sizeof(ev->pubkey), pubkey, sizeof(pubkey)))
+ if (!hex_encode(ev->pubkey, sizeof(ev->pubkey), pubkey))
return 0;
make_cursor(buf, buf + buflen, &cur);
@@ -4112,17 +4103,6 @@ int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce,
tce->command_result.msglen = toksize(tok);
return 1;
- } else if (tok_len == 4 && !memcmp("AUTH", json + tok->start, 4)) {
- tce->evtype = NDB_TCE_AUTH;
-
- tok = &parser.toks[parser.i++];
- if (tok->type != JSMN_STRING)
- return 0;
-
- tce->subid = json + tok->start;
- tce->subid_len = toksize(tok);
-
- return 1;
}
return 0;
diff --git a/nostrdb/nostrdb.h b/nostrdb/nostrdb.h
@@ -85,7 +85,6 @@ enum tce_type {
NDB_TCE_OK = 0x2,
NDB_TCE_NOTICE = 0x3,
NDB_TCE_EOSE = 0x4,
- NDB_TCE_AUTH = 0x5,
};
enum ndb_ingest_filter_action {
diff --git a/nostrdb/print_util.h b/nostrdb/print_util.h
@@ -0,0 +1,36 @@
+
+static void ndb_print_text_search_key(struct ndb_text_search_key *key)
+{
+ printf("K<'%.*s' %d %" PRIu64 " note_id:%" PRIu64 ">", key->str_len, key->str,
+ key->word_index,
+ key->timestamp,
+ key->note_id);
+}
+
+static void print_hex(unsigned char* data, size_t size) {
+ size_t i;
+ for (i = 0; i < size; i++) {
+ printf("%02x", data[i]);
+ }
+}
+
+
+static void ndb_print_text_search_result(struct ndb_txn *txn,
+ struct ndb_text_search_result *r)
+{
+ size_t len;
+ struct ndb_note *note;
+
+ ndb_print_text_search_key(&r->key);
+
+ if (!(note = ndb_get_note_by_key(txn, r->key.note_id, &len))) {
+ printf(": note not found");
+ return;
+ }
+
+ printf(" ");
+ print_hex(ndb_note_id(note), 32);
+
+ printf("\n%s\n\n---\n", ndb_note_content(note));
+}
+
diff --git a/nostrdb/random.h b/nostrdb/random.h
@@ -21,8 +21,7 @@
#include <ntstatus.h>
#include <bcrypt.h>
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-//#include <sys/random.h>
-#include <Security/SecRandom.h>
+#include <sys/random.h>
#elif defined(__OpenBSD__)
#include <unistd.h>
#else
@@ -54,7 +53,7 @@ static int fill_random(unsigned char* data, size_t size) {
#elif defined(__APPLE__) || defined(__OpenBSD__)
/* If `getentropy(2)` is not available you should fallback to either
* `SecRandomCopyBytes` or /dev/urandom */
- int res = SecRandomCopyBytes(kSecRandomDefault, size, data);
+ int res = getentropy(data, size);
if (res == 0) {
return 1;
} else {
diff --git a/nostrdb/sha256.c b/nostrdb/sha256.c
@@ -7,8 +7,8 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.
*/
#include "sha256.h"
-#include "compiler.h"
#include "endian.h"
+#include "compiler.h"
#include <stdbool.h>
#include <assert.h>
#include <string.h>
@@ -16,293 +16,287 @@
static void invalidate_sha256(struct sha256_ctx *ctx)
{
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
- ctx->c.md_len = 0;
+ ctx->c.md_len = 0;
#else
- ctx->bytes = (size_t)-1;
+ ctx->bytes = (size_t)-1;
#endif
}
-static void check_sha256(struct sha256_ctx *ctx UNUSED)
+static void check_sha256(struct sha256_ctx *ctx)
{
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
- assert(ctx->c.md_len != 0);
+ assert(ctx->c.md_len != 0);
#else
- assert(ctx->bytes != (size_t)-1);
+ assert(ctx->bytes != (size_t)-1);
#endif
}
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
void sha256_init(struct sha256_ctx *ctx)
{
- SHA256_Init(&ctx->c);
+ SHA256_Init(&ctx->c);
}
void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size)
{
- check_sha256(ctx);
- SHA256_Update(&ctx->c, p, size);
+ check_sha256(ctx);
+ SHA256_Update(&ctx->c, p, size);
}
void sha256_done(struct sha256_ctx *ctx, struct sha256 *res)
{
- SHA256_Final(res->u.u8, &ctx->c);
- invalidate_sha256(ctx);
+ SHA256_Final(res->u.u8, &ctx->c);
+ invalidate_sha256(ctx);
}
#else
static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
{
- return z ^ (x & (y ^ z));
+ return z ^ (x & (y ^ z));
}
static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
{
- return (x & y) | (z & (x | y));
+ return (x & y) | (z & (x | y));
}
static uint32_t Sigma0(uint32_t x)
{
- return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
+ return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
}
static uint32_t Sigma1(uint32_t x)
{
- return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
+ return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
}
static uint32_t sigma0(uint32_t x)
{
- return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
+ return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
}
static uint32_t sigma1(uint32_t x)
{
- return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
+ return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
}
/** One round of SHA-256. */
static void Round(uint32_t a, uint32_t b, uint32_t c, uint32_t *d, uint32_t e, uint32_t f, uint32_t g, uint32_t *h, uint32_t k, uint32_t w)
{
- uint32_t t1 = *h + Sigma1(e) + Ch(e, f, g) + k + w;
- uint32_t t2 = Sigma0(a) + Maj(a, b, c);
- *d += t1;
- *h = t1 + t2;
+ uint32_t t1 = *h + Sigma1(e) + Ch(e, f, g) + k + w;
+ uint32_t t2 = Sigma0(a) + Maj(a, b, c);
+ *d += t1;
+ *h = t1 + t2;
}
/** Perform one SHA-256 transformation, processing a 64-byte chunk. */
static void Transform(uint32_t *s, const uint32_t *chunk)
{
- uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
- uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
-
- Round(a, b, c, &d, e, f, g, &h, 0x428a2f98, w0 = be32_to_cpu(chunk[0]));
- Round(h, a, b, &c, d, e, f, &g, 0x71374491, w1 = be32_to_cpu(chunk[1]));
- Round(g, h, a, &b, c, d, e, &f, 0xb5c0fbcf, w2 = be32_to_cpu(chunk[2]));
- Round(f, g, h, &a, b, c, d, &e, 0xe9b5dba5, w3 = be32_to_cpu(chunk[3]));
- Round(e, f, g, &h, a, b, c, &d, 0x3956c25b, w4 = be32_to_cpu(chunk[4]));
- Round(d, e, f, &g, h, a, b, &c, 0x59f111f1, w5 = be32_to_cpu(chunk[5]));
- Round(c, d, e, &f, g, h, a, &b, 0x923f82a4, w6 = be32_to_cpu(chunk[6]));
- Round(b, c, d, &e, f, g, h, &a, 0xab1c5ed5, w7 = be32_to_cpu(chunk[7]));
- Round(a, b, c, &d, e, f, g, &h, 0xd807aa98, w8 = be32_to_cpu(chunk[8]));
- Round(h, a, b, &c, d, e, f, &g, 0x12835b01, w9 = be32_to_cpu(chunk[9]));
- Round(g, h, a, &b, c, d, e, &f, 0x243185be, w10 = be32_to_cpu(chunk[10]));
- Round(f, g, h, &a, b, c, d, &e, 0x550c7dc3, w11 = be32_to_cpu(chunk[11]));
- Round(e, f, g, &h, a, b, c, &d, 0x72be5d74, w12 = be32_to_cpu(chunk[12]));
- Round(d, e, f, &g, h, a, b, &c, 0x80deb1fe, w13 = be32_to_cpu(chunk[13]));
- Round(c, d, e, &f, g, h, a, &b, 0x9bdc06a7, w14 = be32_to_cpu(chunk[14]));
- Round(b, c, d, &e, f, g, h, &a, 0xc19bf174, w15 = be32_to_cpu(chunk[15]));
-
- Round(a, b, c, &d, e, f, g, &h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
- Round(h, a, b, &c, d, e, f, &g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
- Round(g, h, a, &b, c, d, e, &f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3));
- Round(f, g, h, &a, b, c, d, &e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4));
- Round(e, f, g, &h, a, b, c, &d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5));
- Round(d, e, f, &g, h, a, b, &c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6));
- Round(c, d, e, &f, g, h, a, &b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7));
- Round(b, c, d, &e, f, g, h, &a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8));
- Round(a, b, c, &d, e, f, g, &h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9));
- Round(h, a, b, &c, d, e, f, &g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10));
- Round(g, h, a, &b, c, d, e, &f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11));
- Round(f, g, h, &a, b, c, d, &e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12));
- Round(e, f, g, &h, a, b, c, &d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13));
- Round(d, e, f, &g, h, a, b, &c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14));
- Round(c, d, e, &f, g, h, a, &b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
- Round(b, c, d, &e, f, g, h, &a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
-
- Round(a, b, c, &d, e, f, g, &h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
- Round(h, a, b, &c, d, e, f, &g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
- Round(g, h, a, &b, c, d, e, &f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3));
- Round(f, g, h, &a, b, c, d, &e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4));
- Round(e, f, g, &h, a, b, c, &d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5));
- Round(d, e, f, &g, h, a, b, &c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6));
- Round(c, d, e, &f, g, h, a, &b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7));
- Round(b, c, d, &e, f, g, h, &a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8));
- Round(a, b, c, &d, e, f, g, &h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9));
- Round(h, a, b, &c, d, e, f, &g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10));
- Round(g, h, a, &b, c, d, e, &f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11));
- Round(f, g, h, &a, b, c, d, &e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12));
- Round(e, f, g, &h, a, b, c, &d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13));
- Round(d, e, f, &g, h, a, b, &c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14));
- Round(c, d, e, &f, g, h, a, &b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
- Round(b, c, d, &e, f, g, h, &a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
-
- Round(a, b, c, &d, e, f, g, &h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
- Round(h, a, b, &c, d, e, f, &g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
- Round(g, h, a, &b, c, d, e, &f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3));
- Round(f, g, h, &a, b, c, d, &e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4));
- Round(e, f, g, &h, a, b, c, &d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5));
- Round(d, e, f, &g, h, a, b, &c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6));
- Round(c, d, e, &f, g, h, a, &b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7));
- Round(b, c, d, &e, f, g, h, &a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8));
- Round(a, b, c, &d, e, f, g, &h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9));
- Round(h, a, b, &c, d, e, f, &g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10));
- Round(g, h, a, &b, c, d, e, &f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11));
- Round(f, g, h, &a, b, c, d, &e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12));
- Round(e, f, g, &h, a, b, c, &d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13));
- Round(d, e, f, &g, h, a, b, &c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14));
- Round(c, d, e, &f, g, h, a, &b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
- Round(b, c, d, &e, f, g, h, &a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));
-
- s[0] += a;
- s[1] += b;
- s[2] += c;
- s[3] += d;
- s[4] += e;
- s[5] += f;
- s[6] += g;
- s[7] += h;
-}
+ uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
+ uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
-static bool alignment_ok(const void *p UNUSED, size_t n UNUSED)
-{
-#if HAVE_UNALIGNED_ACCESS
- return true;
-#else
- return ((size_t)p % n == 0);
-#endif
+ Round(a, b, c, &d, e, f, g, &h, 0x428a2f98, w0 = be32_to_cpu(chunk[0]));
+ Round(h, a, b, &c, d, e, f, &g, 0x71374491, w1 = be32_to_cpu(chunk[1]));
+ Round(g, h, a, &b, c, d, e, &f, 0xb5c0fbcf, w2 = be32_to_cpu(chunk[2]));
+ Round(f, g, h, &a, b, c, d, &e, 0xe9b5dba5, w3 = be32_to_cpu(chunk[3]));
+ Round(e, f, g, &h, a, b, c, &d, 0x3956c25b, w4 = be32_to_cpu(chunk[4]));
+ Round(d, e, f, &g, h, a, b, &c, 0x59f111f1, w5 = be32_to_cpu(chunk[5]));
+ Round(c, d, e, &f, g, h, a, &b, 0x923f82a4, w6 = be32_to_cpu(chunk[6]));
+ Round(b, c, d, &e, f, g, h, &a, 0xab1c5ed5, w7 = be32_to_cpu(chunk[7]));
+ Round(a, b, c, &d, e, f, g, &h, 0xd807aa98, w8 = be32_to_cpu(chunk[8]));
+ Round(h, a, b, &c, d, e, f, &g, 0x12835b01, w9 = be32_to_cpu(chunk[9]));
+ Round(g, h, a, &b, c, d, e, &f, 0x243185be, w10 = be32_to_cpu(chunk[10]));
+ Round(f, g, h, &a, b, c, d, &e, 0x550c7dc3, w11 = be32_to_cpu(chunk[11]));
+ Round(e, f, g, &h, a, b, c, &d, 0x72be5d74, w12 = be32_to_cpu(chunk[12]));
+ Round(d, e, f, &g, h, a, b, &c, 0x80deb1fe, w13 = be32_to_cpu(chunk[13]));
+ Round(c, d, e, &f, g, h, a, &b, 0x9bdc06a7, w14 = be32_to_cpu(chunk[14]));
+ Round(b, c, d, &e, f, g, h, &a, 0xc19bf174, w15 = be32_to_cpu(chunk[15]));
+
+ Round(a, b, c, &d, e, f, g, &h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
+ Round(h, a, b, &c, d, e, f, &g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
+ Round(g, h, a, &b, c, d, e, &f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3));
+ Round(f, g, h, &a, b, c, d, &e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4));
+ Round(e, f, g, &h, a, b, c, &d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5));
+ Round(d, e, f, &g, h, a, b, &c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6));
+ Round(c, d, e, &f, g, h, a, &b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7));
+ Round(b, c, d, &e, f, g, h, &a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8));
+ Round(a, b, c, &d, e, f, g, &h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9));
+ Round(h, a, b, &c, d, e, f, &g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10));
+ Round(g, h, a, &b, c, d, e, &f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11));
+ Round(f, g, h, &a, b, c, d, &e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12));
+ Round(e, f, g, &h, a, b, c, &d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13));
+ Round(d, e, f, &g, h, a, b, &c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14));
+ Round(c, d, e, &f, g, h, a, &b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
+ Round(b, c, d, &e, f, g, h, &a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
+
+ Round(a, b, c, &d, e, f, g, &h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
+ Round(h, a, b, &c, d, e, f, &g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
+ Round(g, h, a, &b, c, d, e, &f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3));
+ Round(f, g, h, &a, b, c, d, &e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4));
+ Round(e, f, g, &h, a, b, c, &d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5));
+ Round(d, e, f, &g, h, a, b, &c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6));
+ Round(c, d, e, &f, g, h, a, &b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7));
+ Round(b, c, d, &e, f, g, h, &a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8));
+ Round(a, b, c, &d, e, f, g, &h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9));
+ Round(h, a, b, &c, d, e, f, &g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10));
+ Round(g, h, a, &b, c, d, e, &f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11));
+ Round(f, g, h, &a, b, c, d, &e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12));
+ Round(e, f, g, &h, a, b, c, &d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13));
+ Round(d, e, f, &g, h, a, b, &c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14));
+ Round(c, d, e, &f, g, h, a, &b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
+ Round(b, c, d, &e, f, g, h, &a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
+
+ Round(a, b, c, &d, e, f, g, &h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
+ Round(h, a, b, &c, d, e, f, &g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
+ Round(g, h, a, &b, c, d, e, &f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3));
+ Round(f, g, h, &a, b, c, d, &e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4));
+ Round(e, f, g, &h, a, b, c, &d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5));
+ Round(d, e, f, &g, h, a, b, &c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6));
+ Round(c, d, e, &f, g, h, a, &b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7));
+ Round(b, c, d, &e, f, g, h, &a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8));
+ Round(a, b, c, &d, e, f, g, &h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9));
+ Round(h, a, b, &c, d, e, f, &g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10));
+ Round(g, h, a, &b, c, d, e, &f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11));
+ Round(f, g, h, &a, b, c, d, &e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12));
+ Round(e, f, g, &h, a, b, c, &d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13));
+ Round(d, e, f, &g, h, a, b, &c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14));
+ Round(c, d, e, &f, g, h, a, &b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
+ Round(b, c, d, &e, f, g, h, &a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));
+
+ s[0] += a;
+ s[1] += b;
+ s[2] += c;
+ s[3] += d;
+ s[4] += e;
+ s[5] += f;
+ s[6] += g;
+ s[7] += h;
}
+
static void add(struct sha256_ctx *ctx, const void *p, size_t len)
{
- const unsigned char *data = p;
- size_t bufsize = ctx->bytes % 64;
-
- if (bufsize + len >= 64) {
- /* Fill the buffer, and process it. */
- memcpy(ctx->buf.u8 + bufsize, data, 64 - bufsize);
- ctx->bytes += 64 - bufsize;
- data += 64 - bufsize;
- len -= 64 - bufsize;
- Transform(ctx->s, ctx->buf.u32);
- bufsize = 0;
- }
-
- while (len >= 64) {
- /* Process full chunks directly from the source. */
- if (alignment_ok(data, sizeof(uint32_t)))
- Transform(ctx->s, (const uint32_t *)data);
- else {
- memcpy(ctx->buf.u8, data, sizeof(ctx->buf));
- Transform(ctx->s, ctx->buf.u32);
- }
- ctx->bytes += 64;
- data += 64;
- len -= 64;
- }
-
- if (len) {
- /* Fill the buffer with what remains. */
- memcpy(ctx->buf.u8 + bufsize, data, len);
- ctx->bytes += len;
- }
+ const unsigned char *data = p;
+ size_t bufsize = ctx->bytes % 64;
+
+ if (bufsize + len >= 64) {
+ /* Fill the buffer, and process it. */
+ memcpy(ctx->buf.u8 + bufsize, data, 64 - bufsize);
+ ctx->bytes += 64 - bufsize;
+ data += 64 - bufsize;
+ len -= 64 - bufsize;
+ Transform(ctx->s, ctx->buf.u32);
+ bufsize = 0;
+ }
+
+ while (len >= 64) {
+ /* Process full chunks directly from the source. */
+ if (alignment_ok(data, sizeof(uint32_t)))
+ Transform(ctx->s, (const uint32_t *)data);
+ else {
+ memcpy(ctx->buf.u8, data, sizeof(ctx->buf));
+ Transform(ctx->s, ctx->buf.u32);
+ }
+ ctx->bytes += 64;
+ data += 64;
+ len -= 64;
+ }
+
+ if (len) {
+ /* Fill the buffer with what remains. */
+ memcpy(ctx->buf.u8 + bufsize, data, len);
+ ctx->bytes += len;
+ }
}
void sha256_init(struct sha256_ctx *ctx)
{
- struct sha256_ctx init = SHA256_INIT;
- *ctx = init;
+ struct sha256_ctx init = SHA256_INIT;
+ *ctx = init;
}
void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size)
{
- check_sha256(ctx);
- add(ctx, p, size);
+ check_sha256(ctx);
+ add(ctx, p, size);
}
void sha256_done(struct sha256_ctx *ctx, struct sha256 *res)
{
- static const unsigned char pad[64] = {0x80};
- uint64_t sizedesc;
- size_t i;
-
- sizedesc = cpu_to_be64((uint64_t)ctx->bytes << 3);
- /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
- add(ctx, pad, 1 + ((128 - 8 - (ctx->bytes % 64) - 1) % 64));
- /* Add number of bits of data (big endian) */
- add(ctx, &sizedesc, 8);
- for (i = 0; i < sizeof(ctx->s) / sizeof(ctx->s[0]); i++)
- res->u.u32[i] = cpu_to_be32(ctx->s[i]);
- invalidate_sha256(ctx);
+ static const unsigned char pad[64] = {0x80};
+ uint64_t sizedesc;
+ size_t i;
+
+ sizedesc = cpu_to_be64((uint64_t)ctx->bytes << 3);
+ /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
+ add(ctx, pad, 1 + ((128 - 8 - (ctx->bytes % 64) - 1) % 64));
+ /* Add number of bits of data (big endian) */
+ add(ctx, &sizedesc, 8);
+ for (i = 0; i < sizeof(ctx->s) / sizeof(ctx->s[0]); i++)
+ res->u.u32[i] = cpu_to_be32(ctx->s[i]);
+ invalidate_sha256(ctx);
}
#endif
void sha256(struct sha256 *sha, const void *p, size_t size)
{
- struct sha256_ctx ctx;
+ struct sha256_ctx ctx;
- sha256_init(&ctx);
- sha256_update(&ctx, p, size);
- sha256_done(&ctx, sha);
+ sha256_init(&ctx);
+ sha256_update(&ctx, p, size);
+ sha256_done(&ctx, sha);
}
-
+
void sha256_u8(struct sha256_ctx *ctx, uint8_t v)
{
- sha256_update(ctx, &v, sizeof(v));
+ sha256_update(ctx, &v, sizeof(v));
}
void sha256_u16(struct sha256_ctx *ctx, uint16_t v)
{
- sha256_update(ctx, &v, sizeof(v));
+ sha256_update(ctx, &v, sizeof(v));
}
void sha256_u32(struct sha256_ctx *ctx, uint32_t v)
{
- sha256_update(ctx, &v, sizeof(v));
+ sha256_update(ctx, &v, sizeof(v));
}
void sha256_u64(struct sha256_ctx *ctx, uint64_t v)
{
- sha256_update(ctx, &v, sizeof(v));
+ sha256_update(ctx, &v, sizeof(v));
}
/* Add as little-endian */
void sha256_le16(struct sha256_ctx *ctx, uint16_t v)
{
- leint16_t lev = cpu_to_le16(v);
- sha256_update(ctx, &lev, sizeof(lev));
+ leint16_t lev = cpu_to_le16(v);
+ sha256_update(ctx, &lev, sizeof(lev));
}
-
+
void sha256_le32(struct sha256_ctx *ctx, uint32_t v)
{
- leint32_t lev = cpu_to_le32(v);
- sha256_update(ctx, &lev, sizeof(lev));
+ leint32_t lev = cpu_to_le32(v);
+ sha256_update(ctx, &lev, sizeof(lev));
}
-
+
void sha256_le64(struct sha256_ctx *ctx, uint64_t v)
{
- leint64_t lev = cpu_to_le64(v);
- sha256_update(ctx, &lev, sizeof(lev));
+ leint64_t lev = cpu_to_le64(v);
+ sha256_update(ctx, &lev, sizeof(lev));
}
/* Add as big-endian */
void sha256_be16(struct sha256_ctx *ctx, uint16_t v)
{
- beint16_t bev = cpu_to_be16(v);
- sha256_update(ctx, &bev, sizeof(bev));
+ beint16_t bev = cpu_to_be16(v);
+ sha256_update(ctx, &bev, sizeof(bev));
}
-
+
void sha256_be32(struct sha256_ctx *ctx, uint32_t v)
{
- beint32_t bev = cpu_to_be32(v);
- sha256_update(ctx, &bev, sizeof(bev));
+ beint32_t bev = cpu_to_be32(v);
+ sha256_update(ctx, &bev, sizeof(bev));
}
-
+
void sha256_be64(struct sha256_ctx *ctx, uint64_t v)
{
- beint64_t bev = cpu_to_be64(v);
- sha256_update(ctx, &bev, sizeof(bev));
+ beint64_t bev = cpu_to_be64(v);
+ sha256_update(ctx, &bev, sizeof(bev));
}
+
+
diff --git a/nostrdb/sha256.h b/nostrdb/sha256.h
@@ -1,7 +1,14 @@
+
#ifndef CCAN_CRYPTO_SHA256_H
#define CCAN_CRYPTO_SHA256_H
+
+
+/** Output length for `wally_sha256` */
+#define SHA256_LEN 32
+
+
/* BSD-MIT - see LICENSE file for details */
-#include "config.h"
+/* #include "config.h" */
#include <stdint.h>
#include <stdlib.h>
@@ -20,17 +27,17 @@
* Other fields may be added to the union in future.
*/
struct sha256 {
- union {
- uint32_t u32[8];
- unsigned char u8[32];
- } u;
+ union {
+ uint32_t u32[8];
+ unsigned char u8[32];
+ } u;
};
/**
* sha256 - return sha256 of an object.
* @sha256: the sha256 to fill in
* @p: pointer to memory,
- * @size: the number of bytes pointed to by
+ * @size: the number of bytes pointed to by @p
*
* The bytes pointed to by @p is SHA256 hashed into @sha256. This is
* equivalent to sha256_init(), sha256_update() then sha256_done().
@@ -42,14 +49,14 @@ void sha256(struct sha256 *sha, const void *p, size_t size);
*/
struct sha256_ctx {
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
- SHA256_CTX c;
+ SHA256_CTX c;
#else
- uint32_t s[8];
- union {
- uint32_t u32[16];
- unsigned char u8[64];
- } buf;
- size_t bytes;
+ uint32_t s[8];
+ union {
+ uint32_t u32[16];
+ unsigned char u8[64];
+ } buf;
+ size_t bytes;
#endif
};
@@ -66,13 +73,13 @@ struct sha256_ctx {
* Example:
* static void hash_all(const char **arr, struct sha256 *hash)
* {
- * size_t i;
- * struct sha256_ctx ctx;
+ * size_t i;
+ * struct sha256_ctx ctx;
*
- * sha256_init(&ctx);
- * for (i = 0; arr[i]; i++)
- * sha256_update(&ctx, arr[i], strlen(arr[i]));
- * sha256_done(&ctx, hash);
+ * sha256_init(&ctx);
+ * for (i = 0; arr[i]; i++)
+ * sha256_update(&ctx, arr[i], strlen(arr[i]));
+ * sha256_done(&ctx, hash);
* }
*/
void sha256_init(struct sha256_ctx *ctx);
@@ -86,33 +93,33 @@ void sha256_init(struct sha256_ctx *ctx);
* Example:
* static void hash_all(const char **arr, struct sha256 *hash)
* {
- * size_t i;
- * struct sha256_ctx ctx = SHA256_INIT;
+ * size_t i;
+ * struct sha256_ctx ctx = SHA256_INIT;
*
- * for (i = 0; arr[i]; i++)
- * sha256_update(&ctx, arr[i], strlen(arr[i]));
- * sha256_done(&ctx, hash);
+ * for (i = 0; arr[i]; i++)
+ * sha256_update(&ctx, arr[i], strlen(arr[i]));
+ * sha256_done(&ctx, hash);
* }
*/
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
-#define SHA256_INIT \
- { { { 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, \
- 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul }, \
- 0x0, 0x0, \
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
- 0x0, 0x20 } }
+#define SHA256_INIT \
+ { { { 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, \
+ 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul }, \
+ 0x0, 0x0, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+ 0x0, 0x20 } }
#else
-#define SHA256_INIT \
- { { 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, \
- 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul }, \
- { { 0 } }, 0 }
+#define SHA256_INIT \
+ { { 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, \
+ 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul }, \
+ { { 0 } }, 0 }
#endif
/**
* sha256_update - include some memory in the hash.
* @ctx: the sha256_ctx to use
* @p: pointer to memory,
- * @size: the number of bytes pointed to by
+ * @size: the number of bytes pointed to by @p
*
* You can call this multiple times to hash more data, before calling
* sha256_done().
@@ -144,4 +151,5 @@ void sha256_le64(struct sha256_ctx *ctx, uint64_t v);
void sha256_be16(struct sha256_ctx *ctx, uint16_t v);
void sha256_be32(struct sha256_ctx *ctx, uint32_t v);
void sha256_be64(struct sha256_ctx *ctx, uint64_t v);
+
#endif /* CCAN_CRYPTO_SHA256_H */
diff --git a/nostrdb/test.c b/nostrdb/test.c
@@ -0,0 +1,1009 @@
+
+#include "nostrdb.h"
+#include "hex.h"
+#include "io.h"
+#include "bolt11/bolt11.h"
+#include "bolt11/amount.h"
+#include "protected_queue.h"
+#include "memchr.h"
+#include "print_util.h"
+#include "bindings/c/profile_reader.h"
+#include "bindings/c/profile_verifier.h"
+#include "bindings/c/meta_reader.h"
+#include "bindings/c/meta_verifier.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static const char *test_dir = "./testdata/db";
+
+static NdbProfile_table_t lookup_profile(struct ndb_txn *txn, uint64_t pk)
+{
+ void *root;
+ size_t len;
+ assert((root = ndb_get_profile_by_key(txn, pk, &len)));
+ assert(root);
+
+ NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root);
+ NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record);
+ return profile;
+}
+
+static void print_search(struct ndb_txn *txn, struct ndb_search *search)
+{
+ NdbProfile_table_t profile = lookup_profile(txn, search->profile_key);
+ const char *name = NdbProfile_name_get(profile);
+ const char *display_name = NdbProfile_display_name_get(profile);
+ printf("searched_name name:'%s' display_name:'%s' pk:%" PRIu64 " ts:%" PRIu64 " id:", name, display_name, search->profile_key, search->key->timestamp);
+ print_hex(search->key->id, 32);
+ printf("\n");
+}
+
+
+static void test_filters()
+{
+ struct ndb_filter filter, *f;
+ struct ndb_note *note;
+ unsigned char buffer[4096];
+
+ const char *test_note = "{\"id\": \"160e76ca67405d7ce9ef7d2dd72f3f36401c8661a73d45498af842d40b01b736\",\"pubkey\": \"67c67870aebc327eb2a2e765e6dbb42f0f120d2c4e4e28dc16b824cf72a5acc1\",\"created_at\": 1700688516,\"kind\": 1337,\"tags\": [[\"t\",\"hashtag\"],[\"t\",\"grownostr\"],[\"p\",\"4d2e7a6a8e08007ace5a03391d21735f45caf1bf3d67b492adc28967ab46525e\"]],\"content\": \"\",\"sig\": \"20c2d070261ed269559ada40ca5ac395c389681ee3b5f7d50de19dd9b328dd70cf27d9d13875e87c968d9b49fa05f66e90f18037be4529b9e582c7e2afac3f06\"}";
+
+ f = &filter;
+ assert(ndb_note_from_json(test_note, strlen(test_note), ¬e, buffer, sizeof(buffer)));
+
+ assert(ndb_filter_init(f));
+ assert(ndb_filter_start_field(f, NDB_FILTER_KINDS));
+ assert(ndb_filter_add_int_element(f, 1337));
+ assert(ndb_filter_add_int_element(f, 2));
+
+ assert(f->current->count == 2);
+ assert(f->current->field.type == NDB_FILTER_KINDS);
+
+ // can't start if we've already started
+ assert(ndb_filter_start_field(f, NDB_FILTER_KINDS) == 0);
+ assert(ndb_filter_start_field(f, NDB_FILTER_GENERIC) == 0);
+ ndb_filter_end_field(f);
+
+ // try matching the filter
+ assert(ndb_filter_matches(f, note));
+
+ _ndb_note_set_kind(note, 1);
+
+ // inverse match
+ assert(!ndb_filter_matches(f, note));
+
+ // should also match 2
+ _ndb_note_set_kind(note, 2);
+ assert(ndb_filter_matches(f, note));
+
+ // don't free, just reset data pointers
+ ndb_filter_reset(f);
+
+ // now try generic matches
+ assert(ndb_filter_start_generic_field(f, 't'));
+ assert(ndb_filter_add_str_element(f, "grownostr"));
+ ndb_filter_end_field(f);
+ assert(ndb_filter_start_field(f, NDB_FILTER_KINDS));
+ assert(ndb_filter_add_int_element(f, 3));
+ ndb_filter_end_field(f);
+
+ // shouldn't match the kind filter
+ assert(!ndb_filter_matches(f, note));
+
+ _ndb_note_set_kind(note, 3);
+
+ // now it should
+ assert(ndb_filter_matches(f, note));
+
+ ndb_filter_reset(f);
+ assert(ndb_filter_start_field(f, NDB_FILTER_AUTHORS));
+ assert(ndb_filter_add_id_element(f, ndb_note_pubkey(note)));
+ ndb_filter_end_field(f);
+ assert(f->current == NULL);
+ assert(ndb_filter_matches(f, note));
+
+ ndb_filter_free(f);
+}
+
+// Test fetched_at profile records. These are saved when new profiles are
+// processed, or the last time we've fetched the profile.
+static void test_fetched_at()
+{
+ struct ndb *ndb;
+ struct ndb_txn txn;
+ uint64_t fetched_at, t1, t2;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ const unsigned char pubkey[] = { 0x87, 0xfb, 0xc6, 0xd5, 0x98, 0x31, 0xa8, 0x23, 0xa4, 0x5d, 0x10, 0x1f,
+ 0x86, 0x94, 0x2c, 0x41, 0xcd, 0xe2, 0x90, 0x23, 0xf4, 0x09, 0x20, 0x24,
+ 0xa2, 0x7c, 0x50, 0x10, 0x3c, 0x15, 0x40, 0x01 };
+
+ const char profile_1[] = "[\"EVENT\",{\"id\": \"a44eb8fb6931d6155b04038bef0624407e46c85c61e5758392cbb615f00184ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593354,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"b\\\"}\",\"sig\": \"7540bbde4b4479275e20d95acaa64027359a73989927f878825093cba2f468bd8e195919a77b4c230acecddf92e6b4bee26918b0c0842f84ec7c1fae82453906\"}]";
+
+ t1 = time(NULL);
+
+ // process the first event, this should set the fetched_at
+ assert(ndb_process_client_event(ndb, profile_1, sizeof(profile_1)));
+
+ // we sleep for a second because we want to make sure the fetched_at is not
+ // updated for the next record, which is an older profile.
+ sleep(1);
+
+ assert(ndb_begin_query(ndb, &txn));
+
+ // this should be set to t1
+ fetched_at = ndb_read_last_profile_fetch(&txn, pubkey);
+
+ assert(fetched_at == t1);
+
+ t2 = time(NULL);
+ assert(t1 != t2); // sanity
+
+ const char profile_2[] = "[\"EVENT\",{\"id\": \"9b2861dda8fc602ec2753f92f1a443c9565de606e0c8f4fd2db4f2506a3b13ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593347,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"a\\\"}\",\"sig\": \"f48da228f8967d33c3caf0a78f853b5144631eb86c7777fd25949123a5272a92765a0963d4686dd0efe05b7a9b986bfac8d43070b234153acbae5006d5a90f31\"}]";
+
+ t2 = time(NULL);
+
+ // process the second event, since this is older it should not change
+ // fetched_at
+ assert(ndb_process_client_event(ndb, profile_2, sizeof(profile_2)));
+
+ // we sleep for a second because we want to make sure the fetched_at is not
+ // updated for the next record, which is an older profile.
+ sleep(1);
+
+ fetched_at = ndb_read_last_profile_fetch(&txn, pubkey);
+ assert(fetched_at == t1);
+}
+
+static void test_reaction_counter()
+{
+ static const int alloc_size = 1024 * 1024;
+ char *json = malloc(alloc_size);
+ struct ndb *ndb;
+ size_t len;
+ void *root;
+ int written, reactions;
+ NdbEventMeta_table_t meta;
+ struct ndb_txn txn;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ read_file("testdata/reactions.json", (unsigned char*)json, alloc_size, &written);
+ assert(ndb_process_client_events(ndb, json, written));
+ ndb_destroy(ndb);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ assert(ndb_begin_query(ndb, &txn));
+
+ const unsigned char id[32] = {
+ 0x1a, 0x41, 0x56, 0x30, 0x31, 0x09, 0xbb, 0x4a, 0x66, 0x0a, 0x6a, 0x90,
+ 0x04, 0xb0, 0xcd, 0xce, 0x8d, 0x83, 0xc3, 0x99, 0x1d, 0xe7, 0x86, 0x4f,
+ 0x18, 0x76, 0xeb, 0x0f, 0x62, 0x2c, 0x68, 0xe8
+ };
+
+ assert((root = ndb_get_note_meta(&txn, id, &len)));
+ assert(0 == NdbEventMeta_verify_as_root(root, len));
+ assert((meta = NdbEventMeta_as_root(root)));
+
+ reactions = NdbEventMeta_reactions_get(meta);
+ //printf("counted reactions: %d\n", reactions);
+ assert(reactions == 2);
+ ndb_end_query(&txn);
+ ndb_destroy(ndb);
+}
+
+static void test_profile_search(struct ndb *ndb)
+{
+ struct ndb_txn txn;
+ struct ndb_search search;
+ int i;
+ const char *name;
+ NdbProfile_table_t profile;
+
+ assert(ndb_begin_query(ndb, &txn));
+ assert(ndb_search_profile(&txn, &search, "jean"));
+ //print_search(&txn, &search);
+ profile = lookup_profile(&txn, search.profile_key);
+ name = NdbProfile_name_get(profile);
+ assert(!strncmp(name, "jean", 4));
+
+ assert(ndb_search_profile_next(&search));
+ //print_search(&txn, &search);
+ profile = lookup_profile(&txn, search.profile_key);
+ name = NdbProfile_name_get(profile);
+ //assert(strncmp(name, "jean", 4));
+
+ for (i = 0; i < 3; i++) {
+ ndb_search_profile_next(&search);
+ //print_search(&txn, &search);
+ }
+
+ //assert(!strcmp(name, "jb55"));
+
+ ndb_search_profile_end(&search);
+ ndb_end_query(&txn);
+}
+
+static void test_profile_updates()
+{
+ static const int alloc_size = 1024 * 1024;
+ char *json = malloc(alloc_size);
+ struct ndb *ndb;
+ size_t len;
+ void *record;
+ int written;
+ struct ndb_txn txn;
+ uint64_t key;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ read_file("testdata/profile-updates.json", (unsigned char*)json, alloc_size, &written);
+
+ assert(ndb_process_client_events(ndb, json, written));
+
+ ndb_destroy(ndb);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ assert(ndb_begin_query(ndb, &txn));
+ const unsigned char pk[32] = {
+ 0x87, 0xfb, 0xc6, 0xd5, 0x98, 0x31, 0xa8, 0x23, 0xa4, 0x5d,
+ 0x10, 0x1f, 0x86, 0x94, 0x2c, 0x41, 0xcd, 0xe2, 0x90, 0x23,
+ 0xf4, 0x09, 0x20, 0x24, 0xa2, 0x7c, 0x50, 0x10, 0x3c, 0x15,
+ 0x40, 0x01
+ };
+ record = ndb_get_profile_by_pubkey(&txn, pk, &len, &key);
+
+ assert(record);
+ int res = NdbProfileRecord_verify_as_root(record, len);
+ assert(res == 0);
+
+ NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(record);
+ NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record);
+ const char *name = NdbProfile_name_get(profile);
+
+ assert(!strcmp(name, "c"));
+
+ ndb_destroy(ndb);
+}
+
+static void test_load_profiles()
+{
+ static const int alloc_size = 1024 * 1024;
+ char *json = malloc(alloc_size);
+ struct ndb *ndb;
+ int written;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ read_file("testdata/profiles.json", (unsigned char*)json, alloc_size, &written);
+
+ assert(ndb_process_events(ndb, json, written));
+
+ ndb_destroy(ndb);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+ unsigned char id[32] = {
+ 0x22, 0x05, 0x0b, 0x6d, 0x97, 0xbb, 0x9d, 0xa0, 0x9e, 0x90, 0xed, 0x0c,
+ 0x6d, 0xd9, 0x5e, 0xed, 0x1d, 0x42, 0x3e, 0x27, 0xd5, 0xcb, 0xa5, 0x94,
+ 0xd2, 0xb4, 0xd1, 0x3a, 0x55, 0x43, 0x09, 0x07 };
+ const char *expected_content = "{\"website\":\"selenejin.com\",\"lud06\":\"\",\"nip05\":\"selenejin@BitcoinNostr.com\",\"picture\":\"https://nostr.build/i/3549697beda0fe1f4ae621f359c639373d92b7c8d5c62582b656c5843138c9ed.jpg\",\"display_name\":\"Selene Jin\",\"about\":\"INTJ | Founding Designer @Blockstream\",\"name\":\"SeleneJin\"}";
+
+ struct ndb_txn txn;
+ assert(ndb_begin_query(ndb, &txn));
+ struct ndb_note *note = ndb_get_note_by_id(&txn, id, NULL, NULL);
+ assert(note != NULL);
+ assert(!strcmp(ndb_note_content(note), expected_content));
+ ndb_end_query(&txn);
+
+ test_profile_search(ndb);
+
+ ndb_destroy(ndb);
+
+ free(json);
+}
+
+static void test_fuzz_events() {
+ struct ndb *ndb;
+ const char *str = "[\"EVENT\"\"\"{\"content\"\"created_at\":0 \"id\"\"5086a8f76fe1da7fb56a25d1bebbafd70fca62e36a72c6263f900ff49b8f8604\"\"kind\":0 \"pubkey\":9c87f94bcbe2a837adc28d46c34eeaab8fc2e1cdf94fe19d4b99ae6a5e6acedc \"sig\"\"27374975879c94658412469cee6db73d538971d21a7b580726a407329a4cafc677fb56b946994cea59c3d9e118fef27e4e61de9d2c46ac0a65df14153 ea93cf5\"\"tags\"[[][\"\"]]}]";
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ ndb_init(&ndb, test_dir, &config);
+ ndb_process_event(ndb, str, strlen(str));
+ ndb_destroy(ndb);
+}
+
+static void test_migrate() {
+ static const char *v0_dir = "testdata/db/v0";
+ struct ndb *ndb;
+ struct ndb_config config;
+ ndb_default_config(&config);
+ ndb_config_set_flags(&config, NDB_FLAG_NOMIGRATE);
+
+ fprintf(stderr, "testing migrate on v0\n");
+ assert(ndb_init(&ndb, v0_dir, &config));
+ assert(ndb_db_version(ndb) == 0);
+ ndb_destroy(ndb);
+
+ ndb_config_set_flags(&config, 0);
+
+ assert(ndb_init(&ndb, v0_dir, &config));
+ ndb_destroy(ndb);
+ assert(ndb_init(&ndb, v0_dir, &config));
+ assert(ndb_db_version(ndb) == 3);
+
+ test_profile_search(ndb);
+ ndb_destroy(ndb);
+}
+
+static void test_basic_event() {
+ unsigned char buf[512];
+ struct ndb_builder builder, *b = &builder;
+ struct ndb_note *note;
+ int ok;
+
+ unsigned char id[32];
+ memset(id, 1, 32);
+
+ unsigned char pubkey[32];
+ memset(pubkey, 2, 32);
+
+ unsigned char sig[64];
+ memset(sig, 3, 64);
+
+ const char *hex_pk = "5d9b81b2d4d5609c5565286fc3b511dc6b9a1b3d7d1174310c624d61d1f82bb9";
+
+ ok = ndb_builder_init(b, buf, sizeof(buf));
+ assert(ok);
+ note = builder.note;
+
+ //memset(note->padding, 3, sizeof(note->padding));
+
+ ok = ndb_builder_set_content(b, hex_pk, strlen(hex_pk)); assert(ok);
+ ndb_builder_set_id(b, id); assert(ok);
+ ndb_builder_set_pubkey(b, pubkey); assert(ok);
+ ndb_builder_set_sig(b, sig); assert(ok);
+
+ ok = ndb_builder_new_tag(b); assert(ok);
+ ok = ndb_builder_push_tag_str(b, "p", 1); assert(ok);
+ ok = ndb_builder_push_tag_str(b, hex_pk, 64); assert(ok);
+
+ ok = ndb_builder_new_tag(b); assert(ok);
+ ok = ndb_builder_push_tag_str(b, "word", 4); assert(ok);
+ ok = ndb_builder_push_tag_str(b, "words", 5); assert(ok);
+ ok = ndb_builder_push_tag_str(b, "w", 1); assert(ok);
+
+ ok = ndb_builder_finalize(b, ¬e, NULL);
+ assert(ok);
+
+ // content should never be packed id
+ // TODO: figure out how to test this now that we don't expose it
+ // assert(note->content.packed.flag != NDB_PACKED_ID);
+ assert(ndb_tags_count(ndb_note_tags(note)) == 2);
+
+ // test iterator
+ struct ndb_iterator iter, *it = &iter;
+
+ ndb_tags_iterate_start(note, it);
+ ok = ndb_tags_iterate_next(it);
+ assert(ok);
+
+ assert(ndb_tag_count(it->tag) == 2);
+ const char *p = ndb_iter_tag_str(it, 0).str;
+ struct ndb_str hpk = ndb_iter_tag_str(it, 1);
+
+ hex_decode(hex_pk, 64, id, 32);
+
+ assert(hpk.flag == NDB_PACKED_ID);
+ assert(memcmp(hpk.id, id, 32) == 0);
+ assert(!strcmp(p, "p"));
+
+ ok = ndb_tags_iterate_next(it);
+ assert(ok);
+ assert(ndb_tag_count(it->tag) == 3);
+ assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word"));
+ assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words"));
+ assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w"));
+
+ ok = ndb_tags_iterate_next(it);
+ assert(!ok);
+}
+
+static void test_empty_tags() {
+ struct ndb_builder builder, *b = &builder;
+ struct ndb_iterator iter, *it = &iter;
+ struct ndb_note *note;
+ int ok;
+ unsigned char buf[1024];
+
+ ok = ndb_builder_init(b, buf, sizeof(buf));
+ assert(ok);
+
+ ok = ndb_builder_finalize(b, ¬e, NULL);
+ assert(ok);
+
+ assert(ndb_tags_count(ndb_note_tags(note)) == 0);
+
+ ndb_tags_iterate_start(note, it);
+ ok = ndb_tags_iterate_next(it);
+ assert(!ok);
+}
+
+static void print_tag(struct ndb_note *note, struct ndb_tag *tag) {
+ struct ndb_str str;
+ int tag_count = ndb_tag_count(tag);
+ for (int i = 0; i < tag_count; i++) {
+ str = ndb_tag_str(note, tag, i);
+ if (str.flag == NDB_PACKED_ID) {
+ printf("<id> ");
+ } else {
+ printf("%s ", str.str);
+ }
+ }
+ printf("\n");
+}
+
+static void test_parse_contact_list()
+{
+ int size, written = 0;
+ unsigned char id[32];
+ static const int alloc_size = 2 << 18;
+ unsigned char *json = malloc(alloc_size);
+ unsigned char *buf = malloc(alloc_size);
+ struct ndb_note *note;
+
+ read_file("testdata/contacts.json", json, alloc_size, &written);
+
+ size = ndb_note_from_json((const char*)json, written, ¬e, buf, alloc_size);
+ printf("ndb_note_from_json size %d\n", size);
+ assert(size > 0);
+ assert(size == 34328);
+
+ memcpy(id, ndb_note_id(note), 32);
+ memset(ndb_note_id(note), 0, 32);
+ assert(ndb_calculate_id(note, json, alloc_size));
+ assert(!memcmp(ndb_note_id(note), id, 32));
+
+ const char* expected_content =
+ "{\"wss://nos.lol\":{\"write\":true,\"read\":true},"
+ "\"wss://relay.damus.io\":{\"write\":true,\"read\":true},"
+ "\"ws://monad.jb55.com:8080\":{\"write\":true,\"read\":true},"
+ "\"wss://nostr.wine\":{\"write\":true,\"read\":true},"
+ "\"wss://welcome.nostr.wine\":{\"write\":true,\"read\":true},"
+ "\"wss://eden.nostr.land\":{\"write\":true,\"read\":true},"
+ "\"wss://relay.mostr.pub\":{\"write\":true,\"read\":true},"
+ "\"wss://nostr-pub.wellorder.net\":{\"write\":true,\"read\":true}}";
+
+ assert(!strcmp(expected_content, ndb_note_content(note)));
+ assert(ndb_note_created_at(note) == 1689904312);
+ assert(ndb_note_kind(note) == 3);
+ assert(ndb_tags_count(ndb_note_tags(note)) == 786);
+ //printf("note content length %d\n", ndb_note_content_length(note));
+ printf("ndb_content_len %d, expected_len %ld\n",
+ ndb_note_content_length(note),
+ strlen(expected_content));
+ assert(ndb_note_content_length(note) == strlen(expected_content));
+
+ struct ndb_iterator iter, *it = &iter;
+ ndb_tags_iterate_start(note, it);
+
+ int tags = 0;
+ int total_elems = 0;
+
+ while (ndb_tags_iterate_next(it)) {
+ total_elems += ndb_tag_count(it->tag);
+ //printf("tag %d: ", tags);
+ if (tags == 0 || tags == 1 || tags == 2)
+ assert(ndb_tag_count(it->tag) == 3);
+
+ if (tags == 6)
+ assert(ndb_tag_count(it->tag) == 2);
+
+ if (tags == 7)
+ assert(!strcmp(ndb_tag_str(note, it->tag, 2).str, "wss://nostr-pub.wellorder.net"));
+
+ if (tags == 786) {
+ static unsigned char h[] = { 0x74, 0xfa, 0xe6, 0x66, 0x4c, 0x9e, 0x79, 0x98, 0x0c, 0x6a, 0xc1, 0x1c, 0x57, 0x75, 0xed, 0x30, 0x93, 0x2b, 0xe9, 0x26, 0xf5, 0xc4, 0x5b, 0xe8, 0xd6, 0x55, 0xe0, 0x0e, 0x35, 0xec, 0xa2, 0x88 };
+ assert(!memcmp(ndb_tag_str(note, it->tag, 1).id, h, 32));
+ }
+
+ //print_tag(it->note, it->tag);
+
+ tags += 1;
+ }
+
+ assert(tags == 786);
+ //printf("total_elems %d\n", total_elems);
+ assert(total_elems == 1580);
+
+ write_file("test_contacts_ndb_note", (unsigned char *)note, size);
+ printf("wrote test_contacts_ndb_note (raw ndb_note)\n");
+
+ free(json);
+ free(buf);
+}
+
+static void test_replacement()
+{
+ static const int alloc_size = 1024 * 1024;
+ char *json = malloc(alloc_size);
+ unsigned char *buf = malloc(alloc_size);
+ struct ndb *ndb;
+ size_t len;
+ int written;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ read_file("testdata/old-new.json", (unsigned char*)json, alloc_size, &written);
+ assert(ndb_process_events(ndb, json, written));
+
+ ndb_destroy(ndb);
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ struct ndb_txn txn;
+ assert(ndb_begin_query(ndb, &txn));
+
+ unsigned char pubkey[32] = { 0x1e, 0x48, 0x9f, 0x6a, 0x4f, 0xc5, 0xc7, 0xac, 0x47, 0x5e, 0xa9, 0x04, 0x17, 0x43, 0xb8, 0x53, 0x11, 0x73, 0x25, 0x92, 0x61, 0xec, 0x71, 0x54, 0x26, 0x41, 0x05, 0x1e, 0x22, 0xa3, 0x82, 0xac };
+
+ void *root = ndb_get_profile_by_pubkey(&txn, pubkey, &len, NULL);
+
+ assert(root);
+ int res = NdbProfileRecord_verify_as_root(root, len);
+ assert(res == 0);
+
+ NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root);
+ NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record);
+ const char *name = NdbProfile_name_get(profile);
+
+ assert(!strcmp(name, "jb55"));
+
+ ndb_end_query(&txn);
+
+ free(json);
+ free(buf);
+}
+
+static void test_fetch_last_noteid()
+{
+ static const int alloc_size = 1024 * 1024;
+ char *json = malloc(alloc_size);
+ unsigned char *buf = malloc(alloc_size);
+ struct ndb *ndb;
+ size_t len;
+ int written;
+ struct ndb_config config;
+ ndb_default_config(&config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ read_file("testdata/random.json", (unsigned char*)json, alloc_size, &written);
+ assert(ndb_process_events(ndb, json, written));
+
+ ndb_destroy(ndb);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ unsigned char id[32] = { 0xdc, 0x96, 0x4f, 0x4c, 0x89, 0x83, 0x64, 0x13, 0x8e, 0x81, 0x96, 0xf0, 0xc7, 0x33, 0x38, 0xc8, 0xcc, 0x3e, 0xbf, 0xa3, 0xaf, 0xdd, 0xbc, 0x7d, 0xd1, 0x58, 0xb4, 0x84, 0x7c, 0x1e, 0xbf, 0xa0 };
+
+ struct ndb_txn txn;
+ assert(ndb_begin_query(ndb, &txn));
+ struct ndb_note *note = ndb_get_note_by_id(&txn, id, &len, NULL);
+ assert(note != NULL);
+ assert(ndb_note_created_at(note) == 1650054135);
+
+ unsigned char pk[32] = { 0x32, 0xe1, 0x82, 0x76, 0x35, 0x45, 0x0e, 0xbb, 0x3c, 0x5a, 0x7d, 0x12, 0xc1, 0xf8, 0xe7, 0xb2, 0xb5, 0x14, 0x43, 0x9a, 0xc1, 0x0a, 0x67, 0xee, 0xf3, 0xd9, 0xfd, 0x9c, 0x5c, 0x68, 0xe2, 0x45 };
+
+ unsigned char profile_note_id[32] = {
+ 0xd1, 0x2c, 0x17, 0xbd, 0xe3, 0x09, 0x4a, 0xd3, 0x2f, 0x4a, 0xb8, 0x62, 0xa6, 0xcc, 0x6f, 0x5c, 0x28, 0x9c, 0xfe, 0x7d, 0x58, 0x02, 0x27, 0x0b, 0xdf, 0x34, 0x90, 0x4d, 0xf5, 0x85, 0xf3, 0x49
+ };
+
+ void *root = ndb_get_profile_by_pubkey(&txn, pk, &len, NULL);
+
+ assert(root);
+ int res = NdbProfileRecord_verify_as_root(root, len);
+ printf("NdbProfileRecord verify result %d\n", res);
+ assert(res == 0);
+
+ NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root);
+ NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record);
+ const char *lnurl = NdbProfileRecord_lnurl_get(profile_record);
+ const char *name = NdbProfile_name_get(profile);
+ uint64_t key = NdbProfileRecord_note_key_get(profile_record);
+ assert(name);
+ assert(lnurl);
+ assert(!strcmp(name, "jb55"));
+ assert(!strcmp(lnurl, "fixme"));
+
+ printf("note_key %" PRIu64 "\n", key);
+
+ struct ndb_note *n = ndb_get_note_by_key(&txn, key, NULL);
+ ndb_end_query(&txn);
+ assert(memcmp(profile_note_id, ndb_note_id(n), 32) == 0);
+
+ //fwrite(profile, len, 1, stdout);
+
+ ndb_destroy(ndb);
+
+ free(json);
+ free(buf);
+}
+
+static void test_parse_contact_event()
+{
+ int written;
+ static const int alloc_size = 2 << 18;
+ char *json = malloc(alloc_size);
+ unsigned char *buf = malloc(alloc_size);
+ struct ndb_tce tce;
+
+ assert(read_file("testdata/contacts-event.json", (unsigned char*)json,
+ alloc_size, &written));
+ assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL));
+
+ assert(tce.evtype == NDB_TCE_EVENT);
+
+ free(json);
+ free(buf);
+}
+
+static void test_content_len()
+{
+ int written;
+ static const int alloc_size = 2 << 18;
+ char *json = malloc(alloc_size);
+ unsigned char *buf = malloc(alloc_size);
+ struct ndb_tce tce;
+
+ assert(read_file("testdata/failed_size.json", (unsigned char*)json,
+ alloc_size, &written));
+ assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL));
+
+ assert(tce.evtype == NDB_TCE_EVENT);
+ assert(ndb_note_content_length(tce.event.note) == 0);
+
+ free(json);
+ free(buf);
+}
+
+static void test_parse_json() {
+ char hex_id[32] = {0};
+ unsigned char buffer[1024];
+ struct ndb_note *note;
+#define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d"
+#define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40"
+ static const char *json =
+ "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}";
+ int ok;
+
+ ok = ndb_note_from_json(json, strlen(json), ¬e, buffer, sizeof(buffer));
+ assert(ok);
+
+ const char *content = ndb_note_content(note);
+ unsigned char *id = ndb_note_id(note);
+
+ hex_decode(HEX_ID, 64, hex_id, sizeof(hex_id));
+
+ assert(!strcmp(content, "共通語"));
+ assert(!memcmp(id, hex_id, 32));
+
+ assert(ndb_tags_count(ndb_note_tags(note)) == 2);
+
+ struct ndb_iterator iter, *it = &iter;
+ ndb_tags_iterate_start(note, it); assert(ok);
+ ok = ndb_tags_iterate_next(it); assert(ok);
+ assert(ndb_tag_count(it->tag) == 2);
+ assert(!strcmp(ndb_iter_tag_str(it, 0).str, "p"));
+ assert(!memcmp(ndb_iter_tag_str(it, 1).id, hex_id, 32));
+
+ ok = ndb_tags_iterate_next(it); assert(ok);
+ assert(ndb_tag_count(it->tag) == 3);
+ assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word"));
+ assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words"));
+ assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w"));
+}
+
+static void test_strings_work_before_finalization() {
+ struct ndb_builder builder, *b = &builder;
+ struct ndb_note *note;
+ int ok;
+ unsigned char buf[1024];
+
+ ok = ndb_builder_init(b, buf, sizeof(buf)); assert(ok);
+ ndb_builder_set_content(b, "hello", 5);
+
+ assert(!strcmp(ndb_note_content(b->note), "hello"));
+ assert(ndb_builder_finalize(b, ¬e, NULL));
+
+ assert(!strcmp(ndb_note_content(note), "hello"));
+}
+
+static void test_tce_eose() {
+ unsigned char buf[1024];
+ const char json[] = "[\"EOSE\",\"s\"]";
+ struct ndb_tce tce;
+ int ok;
+
+ ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL);
+ assert(ok);
+
+ assert(tce.evtype == NDB_TCE_EOSE);
+ assert(tce.subid_len == 1);
+ assert(!memcmp(tce.subid, "s", 1));
+}
+
+static void test_tce_command_result() {
+ unsigned char buf[1024];
+ const char json[] = "[\"OK\",\"\",true,\"blocked: ok\"]";
+ struct ndb_tce tce;
+ int ok;
+
+ ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL);
+ assert(ok);
+
+ assert(tce.evtype == NDB_TCE_OK);
+ assert(tce.subid_len == 0);
+ assert(tce.command_result.ok == 1);
+ assert(!memcmp(tce.subid, "", 0));
+}
+
+static void test_tce_command_result_empty_msg() {
+ unsigned char buf[1024];
+ const char json[] = "[\"OK\",\"b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff\",true,\"\"]";
+ struct ndb_tce tce;
+ int ok;
+
+ ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL);
+ assert(ok);
+
+ assert(tce.evtype == NDB_TCE_OK);
+ assert(tce.subid_len == 64);
+ assert(tce.command_result.ok == 1);
+ assert(tce.command_result.msglen == 0);
+ assert(!memcmp(tce.subid, "b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff", 0));
+}
+
+// test to-client event
+static void test_tce() {
+
+#define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d"
+#define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40"
+#define JSON "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}"
+ unsigned char buf[1024];
+ const char json[] = "[\"EVENT\",\"subid123\"," JSON "]";
+ struct ndb_tce tce;
+ int ok;
+
+ ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL);
+ assert(ok);
+
+ assert(tce.evtype == NDB_TCE_EVENT);
+ assert(tce.subid_len == 8);
+ assert(!memcmp(tce.subid, "subid123", 8));
+
+#undef HEX_ID
+#undef HEX_PK
+#undef JSON
+}
+
+#define TEST_BUF_SIZE 10 // For simplicity
+
+static void test_queue_init_pop_push() {
+ struct prot_queue q;
+ int buffer[TEST_BUF_SIZE];
+ int data;
+
+ // Initialize
+ assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1);
+
+ // Push and Pop
+ data = 5;
+ assert(prot_queue_push(&q, &data) == 1);
+ prot_queue_pop(&q, &data);
+ assert(data == 5);
+
+ // Push to full, and then fail to push
+ for (int i = 0; i < TEST_BUF_SIZE; i++) {
+ assert(prot_queue_push(&q, &i) == 1);
+ }
+ assert(prot_queue_push(&q, &data) == 0); // Should fail as queue is full
+
+ // Pop to empty, and then fail to pop
+ for (int i = 0; i < TEST_BUF_SIZE; i++) {
+ assert(prot_queue_try_pop(&q, &data) == 1);
+ assert(data == i);
+ }
+ assert(prot_queue_try_pop(&q, &data) == 0); // Should fail as queue is empty
+}
+
+// This function will be used by threads to test thread safety.
+void* thread_func(void* arg) {
+ struct prot_queue* q = (struct prot_queue*) arg;
+ int data;
+
+ for (int i = 0; i < 100; i++) {
+ data = i;
+ prot_queue_push(q, &data);
+ prot_queue_pop(q, &data);
+ }
+ return NULL;
+}
+
+static void test_queue_thread_safety() {
+ struct prot_queue q;
+ int buffer[TEST_BUF_SIZE];
+ pthread_t threads[2];
+
+ assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1);
+
+ // Create threads
+ for (int i = 0; i < 2; i++) {
+ pthread_create(&threads[i], NULL, thread_func, &q);
+ }
+
+ // Join threads
+ for (int i = 0; i < 2; i++) {
+ pthread_join(threads[i], NULL);
+ }
+
+ // After all operations, the queue should be empty
+ int data;
+ assert(prot_queue_try_pop(&q, &data) == 0);
+}
+
+static void test_queue_boundary_conditions() {
+ struct prot_queue q;
+ int buffer[TEST_BUF_SIZE];
+ int data;
+
+ // Initialize
+ assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1);
+
+ // Push to full
+ for (int i = 0; i < TEST_BUF_SIZE; i++) {
+ assert(prot_queue_push(&q, &i) == 1);
+ }
+
+ // Try to push to a full queue
+ int old_head = q.head;
+ int old_tail = q.tail;
+ int old_count = q.count;
+ assert(prot_queue_push(&q, &data) == 0);
+
+ // Assert the queue's state has not changed
+ assert(old_head == q.head);
+ assert(old_tail == q.tail);
+ assert(old_count == q.count);
+
+ // Pop to empty
+ for (int i = 0; i < TEST_BUF_SIZE; i++) {
+ assert(prot_queue_try_pop(&q, &data) == 1);
+ }
+
+ // Try to pop from an empty queue
+ old_head = q.head;
+ old_tail = q.tail;
+ old_count = q.count;
+ assert(prot_queue_try_pop(&q, &data) == 0);
+
+ // Assert the queue's state has not changed
+ assert(old_head == q.head);
+ assert(old_tail == q.tail);
+ assert(old_count == q.count);
+}
+
+static void test_fast_strchr()
+{
+ // Test 1: Basic test
+ const char *testStr1 = "Hello, World!";
+ assert(fast_strchr(testStr1, 'W', strlen(testStr1)) == testStr1 + 7);
+
+ // Test 2: Character not present in the string
+ assert(fast_strchr(testStr1, 'X', strlen(testStr1)) == NULL);
+
+ // Test 3: Multiple occurrences of the character
+ const char *testStr2 = "Multiple occurrences.";
+ assert(fast_strchr(testStr2, 'u', strlen(testStr2)) == testStr2 + 1);
+
+ // Test 4: Check with an empty string
+ const char *testStr3 = "";
+ assert(fast_strchr(testStr3, 'a', strlen(testStr3)) == NULL);
+
+ // Test 5: Check with a one-character string
+ const char *testStr4 = "a";
+ assert(fast_strchr(testStr4, 'a', strlen(testStr4)) == testStr4);
+
+ // Test 6: Check the last character in the string
+ const char *testStr5 = "Last character check";
+ assert(fast_strchr(testStr5, 'k', strlen(testStr5)) == testStr5 + 19);
+
+ // Test 7: Large string test (>16 bytes)
+ char *testStr6 = "This is a test for large strings with more than 16 bytes.";
+ assert(fast_strchr(testStr6, 'm', strlen(testStr6)) == testStr6 + 38);
+}
+
+static void test_fulltext()
+{
+ struct ndb *ndb;
+ struct ndb_txn txn;
+ int written;
+ static const int alloc_size = 2 << 18;
+ char *json = malloc(alloc_size);
+ struct ndb_text_search_results results;
+ struct ndb_config config;
+ struct ndb_text_search_config search_config;
+ ndb_default_config(&config);
+ ndb_default_text_search_config(&search_config);
+
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ read_file("testdata/search.json", (unsigned char*)json, alloc_size, &written);
+ assert(ndb_process_client_events(ndb, json, written));
+ ndb_destroy(ndb);
+ assert(ndb_init(&ndb, test_dir, &config));
+
+ ndb_begin_query(ndb, &txn);
+ ndb_text_search(&txn, "Jump Over", &results, &search_config);
+ ndb_end_query(&txn);
+
+ ndb_destroy(ndb);
+
+ free(json);
+}
+
+int main(int argc, const char *argv[]) {
+ test_filters();
+ test_migrate();
+ test_fetched_at();
+ test_profile_updates();
+ test_reaction_counter();
+ test_load_profiles();
+ test_basic_event();
+ test_empty_tags();
+ test_parse_json();
+ test_parse_contact_list();
+ test_strings_work_before_finalization();
+ test_tce();
+ test_tce_command_result();
+ test_tce_eose();
+ test_tce_command_result_empty_msg();
+ test_content_len();
+ test_fuzz_events();
+
+ // note fetching
+ test_fetch_last_noteid();
+
+ // fulltext
+ test_fulltext();
+
+ // protected queue tests
+ test_queue_init_pop_push();
+ test_queue_thread_safety();
+ test_queue_boundary_conditions();
+
+ // memchr stuff
+ test_fast_strchr();
+
+ // profiles
+ test_replacement();
+
+ printf("All tests passed!\n"); // Print this if all tests pass.
+}
+
+
+
diff --git a/nostrdb/threadpool.h b/nostrdb/threadpool.h
@@ -87,7 +87,7 @@ static inline void threadpool_destroy(struct threadpool *tp)
{
struct thread *t;
- for (uint64_t i = 0; i < tp->num_threads; i++) {
+ for (int i = 0; i < tp->num_threads; i++) {
t = &tp->pool[i];
if (!prot_queue_push(&t->inbox, tp->quit_msg)) {
pthread_exit(&t->thread_id);