commit ad0e1f28b7631fe773cddb31cd52c0d961997bde
parent 61051ee853410c0e26be9441bcce2f446d39ba19
Author: William Casarin <jb55@jb55.com>
Date: Fri, 21 Jul 2023 15:14:07 -0700
test: fix build and tests
Diffstat:
8 files changed, 359 insertions(+), 360 deletions(-)
diff --git a/damus-c/nostrdb.c b/damus-c/nostrdb.c
@@ -1,344 +0,0 @@
-
-#include "nostrdb.h"
-#include "jsmn.h"
-#include "hex.h"
-#include "cursor.h"
-#include <stdlib.h>
-
-struct ndb_json_parser {
- const char *json;
- int json_len;
- struct ndb_builder builder;
- jsmn_parser json_parser;
- jsmntok_t *toks, *toks_end;
- int num_tokens;
-};
-
-static inline int cursor_push_tag(struct cursor *cur, struct ndb_tag *tag)
-{
- return cursor_push_u16(cur, tag->count);
-}
-
-int ndb_builder_new(struct ndb_builder *builder, unsigned char *buf,
- int bufsize)
-{
- struct ndb_note *note;
- struct cursor mem;
- int half, size, str_indices_size;
-
- // come on bruh
- if (bufsize < sizeof(struct ndb_note) * 2)
- return 0;
-
- str_indices_size = bufsize / 32;
- size = bufsize - str_indices_size;
- half = size / 2;
-
- //debug("size %d half %d str_indices %d\n", size, half, str_indices_size);
-
- // make a safe cursor of our available memory
- make_cursor(buf, buf + bufsize, &mem);
-
- note = builder->note = (struct ndb_note *)buf;
-
- // take slices of the memory into subcursors
- if (!(cursor_slice(&mem, &builder->note_cur, half) &&
- cursor_slice(&mem, &builder->strings, half) &&
- cursor_slice(&mem, &builder->str_indices, str_indices_size))) {
- return 0;
- }
-
- memset(note, 0, sizeof(*note));
- builder->note_cur.p += sizeof(*note);
-
- note->version = 1;
-
- return 1;
-}
-
-static inline int ndb_json_parser_init(struct ndb_json_parser *p,
- const char *json, int json_len,
- unsigned char *buf, int bufsize)
-{
- int half = bufsize / 2;
-
- unsigned char *tok_start = buf + half;
- unsigned char *tok_end = buf + bufsize;
-
- p->toks = (jsmntok_t*)tok_start;
- p->toks_end = (jsmntok_t*)tok_end;
- p->num_tokens = 0;
- p->json = json;
- p->json_len = json_len;
-
- // ndb_builder gets the first half of the buffer, and jsmn gets the
- // second half. I like this way of alloating memory (without actually
- // dynamically allocating memory). You get one big chunk upfront and
- // then submodules can recursively subdivide it. Maybe you could do
- // something even more clever like golden-ratio style subdivision where
- // the more important stuff gets a larger chunk and then it spirals
- // downward into smaller chunks. Thanks for coming to my TED talk.
-
- if (!ndb_builder_new(&p->builder, buf, half))
- return 0;
-
- jsmn_init(&p->json_parser);
-
- return 1;
-}
-
-static inline int ndb_json_parser_parse(struct ndb_json_parser *p)
-{
- int cap = ((unsigned char *)p->toks_end - (unsigned char*)p->toks)/sizeof(*p->toks);
- p->num_tokens =
- jsmn_parse(&p->json_parser, p->json, p->json_len, p->toks, cap);
-
- return p->num_tokens;
-}
-
-int ndb_builder_finalize(struct ndb_builder *builder, struct ndb_note **note)
-{
- int strings_len = builder->strings.p - builder->strings.start;
- unsigned char *end = builder->note_cur.p + strings_len;
- int total_size = end - builder->note_cur.start;
-
- // move the strings buffer next to the end of our ndb_note
- memmove(builder->note_cur.p, builder->strings.start, strings_len);
-
- // set the strings location
- builder->note->strings = builder->note_cur.p - builder->note_cur.start;
-
- // record the total size
- //builder->note->size = total_size;
-
- *note = builder->note;
-
- return total_size;
-}
-
-struct ndb_note * ndb_builder_note(struct ndb_builder *builder)
-{
- return builder->note;
-}
-
-int ndb_builder_make_string(struct ndb_builder *builder, const char *str,
- int len, union packed_str *pstr)
-{
- uint32_t loc;
-
- if (len == 0) {
- *pstr = ndb_char_to_packed_str(0);
- return 1;
- } else if (len == 1) {
- *pstr = ndb_char_to_packed_str(str[0]);
- return 1;
- } else if (len == 2) {
- *pstr = ndb_chars_to_packed_str(str[0], str[1]);
- return 1;
- }
-
- // find existing matching string to avoid duplicate strings
- int indices = cursor_count(&builder->str_indices, sizeof(uint32_t));
- for (int i = 0; i < indices; i++) {
- uint32_t index = ((uint32_t*)builder->str_indices.start)[i];
- const char *some_str = (const char*)builder->strings.start + index;
-
- if (!strcmp(some_str, str)) {
- // found an existing matching str, use that index
- *pstr = ndb_offset_str(index);
- return 1;
- }
- }
-
- // no string found, push a new one
- loc = builder->strings.p - builder->strings.start;
- if (!(cursor_push(&builder->strings, (unsigned char*)str, len) &&
- cursor_push_byte(&builder->strings, '\0'))) {
- return 0;
- }
- *pstr = ndb_offset_str(loc);
-
- // record in builder indices. ignore return value, if we can't cache it
- // then whatever
- cursor_push_u32(&builder->str_indices, loc);
-
- return 1;
-}
-
-int ndb_builder_set_content(struct ndb_builder *builder, const char *content,
- int len)
-{
- return ndb_builder_make_string(builder, content, len, &builder->note->content);
-}
-
-
-static inline int jsoneq(const char *json, jsmntok_t *tok, int tok_len,
- const char *s)
-{
- if (tok->type == JSMN_STRING && (int)strlen(s) == tok_len &&
- memcmp(json + tok->start, s, tok_len) == 0) {
- return 1;
- }
- return 0;
-}
-
-static inline int toksize(jsmntok_t *tok)
-{
- return tok->end - tok->start;
-}
-
-// Push a json array into an ndb tag ["p", "abcd..."] -> struct ndb_tag
-static inline int ndb_builder_tag_from_json_array(struct ndb_json_parser *p,
- jsmntok_t *array)
-{
- jsmntok_t *str_tok;
- const char *str;
-
- if (array->size == 0)
- return 0;
-
- if (!ndb_builder_new_tag(&p->builder))
- return 0;
-
- for (int i = 0; i < array->size; i++) {
- str_tok = &array[i+1];
- str = p->json + str_tok->start;
-
- if (!ndb_builder_push_tag_str(&p->builder, str, toksize(str_tok)))
- return 0;
- }
-
- return 1;
-}
-
-// Push json tags into ndb data
-// [["t", "hashtag"], ["p", "abcde..."]] -> struct ndb_tags
-static inline int ndb_builder_process_json_tags(struct ndb_json_parser *p,
- jsmntok_t *array)
-{
- jsmntok_t *tag = array;
-
- if (array->size == 0)
- return 1;
-
- for (int i = 0; i < array->size; i++) {
- if (!ndb_builder_tag_from_json_array(p, &tag[i+1]))
- return 0;
- tag += tag[i+1].size;
- }
-
- return 1;
-}
-
-
-int ndb_note_from_json(const char *json, int len, struct ndb_note **note,
- unsigned char *buf, int bufsize)
-{
- jsmntok_t *tok = NULL;
- unsigned char hexbuf[64];
-
- int i, tok_len, res;
- const char *start;
- struct ndb_json_parser parser;
-
- ndb_json_parser_init(&parser, json, len, buf, bufsize);
- res = ndb_json_parser_parse(&parser);
- if (res < 0)
- return res;
-
- if (parser.num_tokens < 1 || parser.toks[0].type != JSMN_OBJECT)
- return 0;
-
- for (i = 1; i < parser.num_tokens; i++) {
- tok = &parser.toks[i];
- start = json + tok->start;
- tok_len = toksize(tok);
-
- //printf("toplevel %.*s %d\n", tok_len, json + tok->start, tok->type);
- if (tok_len == 0 || i + 1 >= parser.num_tokens)
- continue;
-
- if (start[0] == 'p' && jsoneq(json, tok, tok_len, "pubkey")) {
- // pubkey
- tok = &parser.toks[i+1];
- hex_decode(json + tok->start, toksize(tok), hexbuf, sizeof(hexbuf));
- ndb_builder_set_pubkey(&parser.builder, hexbuf);
- } else if (tok_len == 2 && start[0] == 'i' && start[1] == 'd') {
- // id
- tok = &parser.toks[i+1];
- hex_decode(json + tok->start, toksize(tok), hexbuf, sizeof(hexbuf));
- // TODO: validate id
- ndb_builder_set_id(&parser.builder, hexbuf);
- } else if (tok_len == 3 && start[0] == 's' && start[1] == 'i' && start[2] == 'g') {
- // sig
- tok = &parser.toks[i+1];
- hex_decode(json + tok->start, toksize(tok), hexbuf, sizeof(hexbuf));
- ndb_builder_set_signature(&parser.builder, hexbuf);
- } else if (start[0] == 'k' && jsoneq(json, tok, tok_len, "kind")) {
- // kind
- tok = &parser.toks[i+1];
- printf("json_kind %.*s\n", toksize(tok), json + tok->start);
- } else if (start[0] == 'c') {
- if (jsoneq(json, tok, tok_len, "created_at")) {
- // created_at
- tok = &parser.toks[i+1];
- printf("json_created_at %.*s\n", toksize(tok), json + tok->start);
- } else if (jsoneq(json, tok, tok_len, "content")) {
- // content
- tok = &parser.toks[i+1];
- if (!ndb_builder_set_content(&parser.builder, json + tok->start, toksize(tok)))
- return 0;
- }
- } else if (start[0] == 't' && jsoneq(json, tok, tok_len, "tags")) {
- tok = &parser.toks[i+1];
- ndb_builder_process_json_tags(&parser, tok);
- i += tok->size;
- }
- }
-
- return ndb_builder_finalize(&parser.builder, note);
-}
-
-void ndb_builder_set_pubkey(struct ndb_builder *builder, unsigned char *pubkey)
-{
- memcpy(builder->note->pubkey, pubkey, 32);
-}
-
-void ndb_builder_set_id(struct ndb_builder *builder, unsigned char *id)
-{
- memcpy(builder->note->id, id, 32);
-}
-
-void ndb_builder_set_signature(struct ndb_builder *builder,
- unsigned char *signature)
-{
- memcpy(builder->note->signature, signature, 64);
-}
-
-void ndb_builder_set_kind(struct ndb_builder *builder, uint32_t kind)
-{
- builder->note->kind = kind;
-}
-
-int ndb_builder_new_tag(struct ndb_builder *builder)
-{
- builder->note->tags.count++;
- struct ndb_tag tag = {0};
- builder->current_tag = (struct ndb_tag *)builder->note_cur.p;
- return cursor_push_tag(&builder->note_cur, &tag);
-}
-
-/// Push an element to the current tag
-///
-/// Basic idea is to call ndb_builder_new_tag
-inline int ndb_builder_push_tag_str(struct ndb_builder *builder,
- const char *str, int len)
-{
- union packed_str pstr;
- if (!ndb_builder_make_string(builder, str, len, &pstr))
- return 0;
- if (!cursor_push_u32(&builder->note_cur, pstr.offset))
- return 0;
- builder->current_tag->count++;
- return 1;
-}
diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj
@@ -278,6 +278,7 @@
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879542996BAB900F758CC /* RelayPaidDetail.swift */; };
4CE879582996C45300F758CC /* ZapsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879572996C45300F758CC /* ZapsView.swift */; };
4CE8795B2996C47A00F758CC /* ZapsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8795A2996C47A00F758CC /* ZapsModel.swift */; };
+ 4CE9FBBA2A6B3C63007E485C /* nostrdb.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CE9FBB82A6B3B26007E485C /* nostrdb.c */; };
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; };
4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; };
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; };
@@ -772,6 +773,8 @@
4CE879542996BAB900F758CC /* RelayPaidDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPaidDetail.swift; sourceTree = "<group>"; };
4CE879572996C45300F758CC /* ZapsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapsView.swift; sourceTree = "<group>"; };
4CE8795A2996C47A00F758CC /* ZapsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapsModel.swift; sourceTree = "<group>"; };
+ 4CE9FBB82A6B3B26007E485C /* nostrdb.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nostrdb.c; sourceTree = "<group>"; };
+ 4CE9FBB92A6B3B26007E485C /* nostrdb.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nostrdb.h; sourceTree = "<group>"; };
4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; };
4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; };
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; };
@@ -907,8 +910,6 @@
4CA9276F2A2A5D470098A105 /* parser.h */,
4CA9276E2A2A5D110098A105 /* wasm.c */,
4CA9276D2A2A5D110098A105 /* wasm.h */,
- 4C4F14A82A2A71AB0045A0B9 /* nostrscript.h */,
- 4C4F14A92A2A71AB0045A0B9 /* nostrscript.c */,
4C06670928FDE64700038D2A /* damus.h */,
4C06670A28FDE64700038D2A /* damus.c */,
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */,
@@ -1067,6 +1068,8 @@
4C19AE4B2A5CEF7C00C90DB7 /* primal.ts */,
4C19AE4C2A5CEF7C00C90DB7 /* NostrScript.swift */,
4C19AE502A5CEF7C00C90DB7 /* nostr.ts */,
+ 4C4F14A82A2A71AB0045A0B9 /* nostrscript.h */,
+ 4C4F14A92A2A71AB0045A0B9 /* nostrscript.c */,
);
path = nostrscript;
sourceTree = "<group>";
@@ -1320,23 +1323,17 @@
4C9054862A6AEB4500811EEC /* nostrdb */ = {
isa = PBXGroup;
children = (
- 4C9054892A6AEDCD00811EEC /* Tests */,
4C9054882A6AED4700811EEC /* NdbTagIterator.swift */,
4C90548A2A6AEDEE00811EEC /* NdbNote.swift */,
4C5D5C9C2A6B2CB40024563C /* AsciiCharacter.swift */,
4CDD1ADF2A6B305F001CD4DF /* NdbTagElem.swift */,
4CDD1AE12A6B3074001CD4DF /* NdbTagsIterator.swift */,
+ 4CE9FBB82A6B3B26007E485C /* nostrdb.c */,
+ 4CE9FBB92A6B3B26007E485C /* nostrdb.h */,
);
path = nostrdb;
sourceTree = "<group>";
};
- 4C9054892A6AEDCD00811EEC /* Tests */ = {
- isa = PBXGroup;
- children = (
- );
- path = Tests;
- sourceTree = "<group>";
- };
4C9B0DEC2A65A74000CBDA21 /* Util */ = {
isa = PBXGroup;
children = (
@@ -1880,6 +1877,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 4CE9FBBA2A6B3C63007E485C /* nostrdb.c in Sources */,
4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */,
5CF72FC229B9142F00124A13 /* ShareAction.swift in Sources */,
4C8D1A6C29F1DFC200ACDF75 /* FriendIcon.swift in Sources */,
diff --git a/damusTests/NDB/NdbTests.swift b/damusTests/NDB/NdbTests.swift
@@ -26,13 +26,13 @@ final class NdbTests: XCTestCase {
let id = "20d0ff27d6fcb13de8366328c5b1a7af26bcac07f2e558fbebd5e9242e608c09"
XCTAssertEqual(hex_encode(note.id), id)
- XCTAssertEqual(note.tags().underestimatedCount, 786)
- XCTAssertEqual(note.tags().underestimatedCount, 786)
+ XCTAssertEqual(note.tags().reduce(0, { sum, _ in sum + 1 }), 786)
+ XCTAssertEqual(note.tags().reduce(0, { sum, _ in sum + 1 }), 786)
//let tags = note.tags()
for tag in note.tags() {
for elem in tag {
- print("test_ndb_iterator \(elem.)")
+ print("test_ndb_iterator \(elem.string())")
}
}
diff --git a/nostrdb/NdbNote.swift b/nostrdb/NdbNote.swift
@@ -30,13 +30,13 @@ struct NdbNote {
var note: UnsafeMutablePointer<ndb_note>?
- let len = data.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) -> Int in
- return Int(ndb_note_from_json(&json_cstr, Int32(json_cstr.count), ¬e, bytes.baseAddress, Int32(bufsize)))
+ let len = data.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) in
+ return ndb_note_from_json(&json_cstr, Int32(json_cstr.count), ¬e, bytes.baseAddress, Int32(bufsize))
}
guard let note else { return nil }
// Create new Data with just the valid bytes
- let validData = Data(bytes: ¬e.pointee, count: len)
+ let validData = Data(bytes: ¬e.pointee, count: Int(len))
return NdbNote(notePointer: note, data: validData)
}}
diff --git a/nostrdb/nostrdb.c b/nostrdb/nostrdb.c
@@ -0,0 +1,345 @@
+
+#include "nostrdb.h"
+#include "jsmn.h"
+#include "hex.h"
+#include "cursor.h"
+#include <stdlib.h>
+
+struct ndb_json_parser {
+ const char *json;
+ int json_len;
+ struct ndb_builder builder;
+ jsmn_parser json_parser;
+ jsmntok_t *toks, *toks_end;
+ int num_tokens;
+};
+
+static inline int cursor_push_tag(struct cursor *cur, struct ndb_tag *tag)
+{
+ return cursor_push_u16(cur, tag->count);
+}
+
+int ndb_builder_new(struct ndb_builder *builder, unsigned char *buf,
+ int bufsize)
+{
+ struct ndb_note *note;
+ struct cursor mem;
+ int half, size, str_indices_size;
+
+ // come on bruh
+ if (bufsize < sizeof(struct ndb_note) * 2)
+ return 0;
+
+ str_indices_size = bufsize / 32;
+ size = bufsize - str_indices_size;
+ half = size / 2;
+
+ //debug("size %d half %d str_indices %d\n", size, half, str_indices_size);
+
+ // make a safe cursor of our available memory
+ make_cursor(buf, buf + bufsize, &mem);
+
+ note = builder->note = (struct ndb_note *)buf;
+
+ // take slices of the memory into subcursors
+ if (!(cursor_slice(&mem, &builder->note_cur, half) &&
+ cursor_slice(&mem, &builder->strings, half) &&
+ cursor_slice(&mem, &builder->str_indices, str_indices_size))) {
+ return 0;
+ }
+
+ memset(note, 0, sizeof(*note));
+ builder->note_cur.p += sizeof(*note);
+
+ note->version = 1;
+
+ return 1;
+}
+
+static inline int ndb_json_parser_init(struct ndb_json_parser *p,
+ const char *json, int json_len,
+ unsigned char *buf, int bufsize)
+{
+ int half = bufsize / 2;
+
+ unsigned char *tok_start = buf + half;
+ unsigned char *tok_end = buf + bufsize;
+
+ p->toks = (jsmntok_t*)tok_start;
+ p->toks_end = (jsmntok_t*)tok_end;
+ p->num_tokens = 0;
+ p->json = json;
+ p->json_len = json_len;
+
+ // ndb_builder gets the first half of the buffer, and jsmn gets the
+ // second half. I like this way of alloating memory (without actually
+ // dynamically allocating memory). You get one big chunk upfront and
+ // then submodules can recursively subdivide it. Maybe you could do
+ // something even more clever like golden-ratio style subdivision where
+ // the more important stuff gets a larger chunk and then it spirals
+ // downward into smaller chunks. Thanks for coming to my TED talk.
+
+ if (!ndb_builder_new(&p->builder, buf, half))
+ return 0;
+
+ jsmn_init(&p->json_parser);
+
+ return 1;
+}
+
+static inline int ndb_json_parser_parse(struct ndb_json_parser *p)
+{
+ int cap = ((unsigned char *)p->toks_end - (unsigned char*)p->toks)/sizeof(*p->toks);
+ p->num_tokens =
+ jsmn_parse(&p->json_parser, p->json, p->json_len, p->toks, cap);
+
+ return p->num_tokens;
+}
+
+int ndb_builder_finalize(struct ndb_builder *builder, struct ndb_note **note)
+{
+ int strings_len = builder->strings.p - builder->strings.start;
+ unsigned char *end = builder->note_cur.p + strings_len;
+ int total_size = end - builder->note_cur.start;
+
+ // move the strings buffer next to the end of our ndb_note
+ memmove(builder->note_cur.p, builder->strings.start, strings_len);
+
+ // set the strings location
+ builder->note->strings = builder->note_cur.p - builder->note_cur.start;
+
+ // record the total size
+ //builder->note->size = total_size;
+
+ *note = builder->note;
+
+ return total_size;
+}
+
+struct ndb_note * ndb_builder_note(struct ndb_builder *builder)
+{
+ return builder->note;
+}
+
+int ndb_builder_make_string(struct ndb_builder *builder, const char *str,
+ int len, union packed_str *pstr)
+{
+ uint32_t loc;
+
+ if (len == 0) {
+ *pstr = ndb_char_to_packed_str(0);
+ return 1;
+ } else if (len == 1) {
+ *pstr = ndb_char_to_packed_str(str[0]);
+ return 1;
+ } else if (len == 2) {
+ *pstr = ndb_chars_to_packed_str(str[0], str[1]);
+ return 1;
+ }
+
+ // find existing matching string to avoid duplicate strings
+ int indices = cursor_count(&builder->str_indices, sizeof(uint32_t));
+ for (int i = 0; i < indices; i++) {
+ uint32_t index = ((uint32_t*)builder->str_indices.start)[i];
+ const char *some_str = (const char*)builder->strings.start + index;
+
+ if (!strcmp(some_str, str)) {
+ // found an existing matching str, use that index
+ *pstr = ndb_offset_str(index);
+ return 1;
+ }
+ }
+
+ // no string found, push a new one
+ loc = builder->strings.p - builder->strings.start;
+ if (!(cursor_push(&builder->strings, (unsigned char*)str, len) &&
+ cursor_push_byte(&builder->strings, '\0'))) {
+ return 0;
+ }
+ *pstr = ndb_offset_str(loc);
+
+ // record in builder indices. ignore return value, if we can't cache it
+ // then whatever
+ cursor_push_u32(&builder->str_indices, loc);
+
+ return 1;
+}
+
+int ndb_builder_set_content(struct ndb_builder *builder, const char *content,
+ int len)
+{
+ return ndb_builder_make_string(builder, content, len, &builder->note->content);
+}
+
+
+static inline int jsoneq(const char *json, jsmntok_t *tok, int tok_len,
+ const char *s)
+{
+ if (tok->type == JSMN_STRING && (int)strlen(s) == tok_len &&
+ memcmp(json + tok->start, s, tok_len) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static inline int toksize(jsmntok_t *tok)
+{
+ return tok->end - tok->start;
+}
+
+// Push a json array into an ndb tag ["p", "abcd..."] -> struct ndb_tag
+static inline int ndb_builder_tag_from_json_array(struct ndb_json_parser *p,
+ jsmntok_t *array)
+{
+ jsmntok_t *str_tok;
+ const char *str;
+
+ if (array->size == 0)
+ return 0;
+
+ if (!ndb_builder_new_tag(&p->builder))
+ return 0;
+
+ for (int i = 0; i < array->size; i++) {
+ str_tok = &array[i+1];
+ str = p->json + str_tok->start;
+
+ if (!ndb_builder_push_tag_str(&p->builder, str, toksize(str_tok)))
+ return 0;
+ }
+
+ return 1;
+}
+
+// Push json tags into ndb data
+// [["t", "hashtag"], ["p", "abcde..."]] -> struct ndb_tags
+static inline int ndb_builder_process_json_tags(struct ndb_json_parser *p,
+ jsmntok_t *array)
+{
+ jsmntok_t *tag = array;
+
+ if (array->size == 0)
+ return 1;
+
+ for (int i = 0; i < array->size; i++) {
+ if (!ndb_builder_tag_from_json_array(p, &tag[i+1]))
+ return 0;
+ tag += tag[i+1].size;
+ }
+
+ return 1;
+}
+
+
+
+int ndb_note_from_json(const char *json, int len, struct ndb_note **note,
+ unsigned char *buf, int bufsize)
+{
+ jsmntok_t *tok = NULL;
+ unsigned char hexbuf[64];
+
+ int i, tok_len, res;
+ const char *start;
+ struct ndb_json_parser parser;
+
+ ndb_json_parser_init(&parser, json, len, buf, bufsize);
+ res = ndb_json_parser_parse(&parser);
+ if (res < 0)
+ return res;
+
+ if (parser.num_tokens < 1 || parser.toks[0].type != JSMN_OBJECT)
+ return 0;
+
+ for (i = 1; i < parser.num_tokens; i++) {
+ tok = &parser.toks[i];
+ start = json + tok->start;
+ tok_len = toksize(tok);
+
+ //printf("toplevel %.*s %d\n", tok_len, json + tok->start, tok->type);
+ if (tok_len == 0 || i + 1 >= parser.num_tokens)
+ continue;
+
+ if (start[0] == 'p' && jsoneq(json, tok, tok_len, "pubkey")) {
+ // pubkey
+ tok = &parser.toks[i+1];
+ hex_decode(json + tok->start, toksize(tok), hexbuf, sizeof(hexbuf));
+ ndb_builder_set_pubkey(&parser.builder, hexbuf);
+ } else if (tok_len == 2 && start[0] == 'i' && start[1] == 'd') {
+ // id
+ tok = &parser.toks[i+1];
+ hex_decode(json + tok->start, toksize(tok), hexbuf, sizeof(hexbuf));
+ // TODO: validate id
+ ndb_builder_set_id(&parser.builder, hexbuf);
+ } else if (tok_len == 3 && start[0] == 's' && start[1] == 'i' && start[2] == 'g') {
+ // sig
+ tok = &parser.toks[i+1];
+ hex_decode(json + tok->start, toksize(tok), hexbuf, sizeof(hexbuf));
+ ndb_builder_set_signature(&parser.builder, hexbuf);
+ } else if (start[0] == 'k' && jsoneq(json, tok, tok_len, "kind")) {
+ // kind
+ tok = &parser.toks[i+1];
+ printf("json_kind %.*s\n", toksize(tok), json + tok->start);
+ } else if (start[0] == 'c') {
+ if (jsoneq(json, tok, tok_len, "created_at")) {
+ // created_at
+ tok = &parser.toks[i+1];
+ printf("json_created_at %.*s\n", toksize(tok), json + tok->start);
+ } else if (jsoneq(json, tok, tok_len, "content")) {
+ // content
+ tok = &parser.toks[i+1];
+ if (!ndb_builder_set_content(&parser.builder, json + tok->start, toksize(tok)))
+ return 0;
+ }
+ } else if (start[0] == 't' && jsoneq(json, tok, tok_len, "tags")) {
+ tok = &parser.toks[i+1];
+ ndb_builder_process_json_tags(&parser, tok);
+ i += tok->size;
+ }
+ }
+
+ return ndb_builder_finalize(&parser.builder, note);
+}
+
+void ndb_builder_set_pubkey(struct ndb_builder *builder, unsigned char *pubkey)
+{
+ memcpy(builder->note->pubkey, pubkey, 32);
+}
+
+void ndb_builder_set_id(struct ndb_builder *builder, unsigned char *id)
+{
+ memcpy(builder->note->id, id, 32);
+}
+
+void ndb_builder_set_signature(struct ndb_builder *builder,
+ unsigned char *signature)
+{
+ memcpy(builder->note->signature, signature, 64);
+}
+
+void ndb_builder_set_kind(struct ndb_builder *builder, uint32_t kind)
+{
+ builder->note->kind = kind;
+}
+
+int ndb_builder_new_tag(struct ndb_builder *builder)
+{
+ builder->note->tags.count++;
+ struct ndb_tag tag = {0};
+ builder->current_tag = (struct ndb_tag *)builder->note_cur.p;
+ return cursor_push_tag(&builder->note_cur, &tag);
+}
+
+/// Push an element to the current tag
+///
+/// Basic idea is to call ndb_builder_new_tag
+inline int ndb_builder_push_tag_str(struct ndb_builder *builder,
+ const char *str, int len)
+{
+ union packed_str pstr;
+ if (!ndb_builder_make_string(builder, str, len, &pstr))
+ return 0;
+ if (!cursor_push_u32(&builder->note_cur, pstr.offset))
+ return 0;
+ builder->current_tag->count++;
+ return 1;
+}
diff --git a/damus-c/nostrdb.h b/nostrdb/nostrdb.h
diff --git a/damus-c/nostrscript.c b/nostrscript/nostrscript.c
diff --git a/damus-c/nostrscript.h b/nostrscript/nostrscript.h