damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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:
Anostrdb/Makefile | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/bench-ingest-many.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/bench.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb/bindings/c/flatbuffers_common_builder.h | 6+++---
Mnostrdb/bindings/c/flatbuffers_common_reader.h | 6+++---
Mnostrdb/bindings/c/meta_builder.h | 4++--
Mnostrdb/bindings/c/meta_json_parser.h | 6+++---
Mnostrdb/bindings/c/meta_reader.h | 6+++---
Mnostrdb/bindings/c/meta_verifier.h | 6+++---
Mnostrdb/bindings/c/profile_builder.h | 4++--
Mnostrdb/bindings/c/profile_json_parser.h | 6+++---
Mnostrdb/bindings/c/profile_reader.h | 6+++---
Mnostrdb/bindings/c/profile_verifier.h | 6+++---
Anostrdb/bindings/rust/.dir | 0
Anostrdb/bindings/rust/ndb_meta.rs | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/bindings/rust/ndb_profile.rs | 514+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb/bindings/swift/NdbProfile.swift | 2+-
Anostrdb/bolt11/bech32.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/bolt11/bech32.h | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb/config.h | 4++--
Anostrdb/configurator.c | 1110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb/endian.h | 116++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mnostrdb/hex.h | 124++++++++++++++++++++++++++++++++++---------------------------------------------
Anostrdb/io.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/lmdb_util.h | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/ndb.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb/nostr_bech32.c | 19-------------------
Mnostrdb/nostr_bech32.h | 5-----
Mnostrdb/nostrdb.c | 28++++------------------------
Mnostrdb/nostrdb.h | 1-
Anostrdb/print_util.h | 36++++++++++++++++++++++++++++++++++++
Mnostrdb/random.h | 5++---
Mnostrdb/sha256.c | 354+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mnostrdb/sha256.h | 78+++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Anostrdb/test.c | 1009+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb/threadpool.h | 2+-
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, &note, 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, &times);\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), &note, 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, &note, 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, &note, 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, &note, 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), &note, 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, &note, 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);