damus

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

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:
Ddamus-c/nostrdb.c | 344-------------------------------------------------------------------------------
Mdamus.xcodeproj/project.pbxproj | 18++++++++----------
MdamusTests/NDB/NdbTests.swift | 6+++---
Mnostrdb/NdbNote.swift | 6+++---
Anostrdb/nostrdb.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rdamus-c/nostrdb.h -> nostrdb/nostrdb.h | 0
Rdamus-c/nostrscript.c -> nostrscript/nostrscript.c | 0
Rdamus-c/nostrscript.h -> nostrscript/nostrscript.h | 0
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), &note, bytes.baseAddress, Int32(bufsize))) + let len = data.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) in + return ndb_note_from_json(&json_cstr, Int32(json_cstr.count), &note, bytes.baseAddress, Int32(bufsize)) } guard let note else { return nil } // Create new Data with just the valid bytes - let validData = Data(bytes: &note.pointee, count: len) + let validData = Data(bytes: &note.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