ndb: switch to nostrdb notes

This is a refactor of the codebase to use a more memory-efficient
representation of notes. It should also be much faster at decoding since
we're using a custom C json parser now.

Changelog-Changed: Improved memory usage and performance when processing events
This commit is contained in:
William Casarin
2023-07-26 08:46:44 -07:00
parent 55bbe8f855
commit cebd1f48ca
110 changed files with 2153 additions and 1799 deletions
+69 -39
View File
@@ -13,7 +13,12 @@ struct CommandResult {
let msg: String
}
enum NostrResponse: Decodable {
enum MaybeResponse {
case bad
case ok(NostrResponse)
}
enum NostrResponse {
case event(String, NostrEvent)
case notice(String)
case eose(String)
@@ -32,48 +37,73 @@ enum NostrResponse: Decodable {
}
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
static func owned_from_json(json: String) -> NostrResponse? {
return json.withCString{ cstr in
let bufsize: Int = max(Int(Double(json.utf8.count) * 2.0), Int(getpagesize()))
let data = malloc(bufsize)
// Only use first item
let typ = try container.decode(String.self)
if typ == "EVENT" {
let sub_id = try container.decode(String.self)
var ev: NostrEvent
do {
ev = try container.decode(NostrEvent.self)
} catch {
print(error)
throw error
if data == nil {
let r: NostrResponse? = nil
return r
}
//ev.pow = count_hash_leading_zero_bits(ev.id)
self = .event(sub_id, ev)
return
} else if typ == "NOTICE" {
let msg = try container.decode(String.self)
self = .notice(msg)
return
} else if typ == "EOSE" {
let sub_id = try container.decode(String.self)
self = .eose(sub_id)
return
} else if typ == "OK" {
var cr: CommandResult
do {
let event_id = try container.decode(String.self)
let ok = try container.decode(Bool.self)
let msg = try container.decode(String.self)
cr = CommandResult(event_id: event_id, ok: ok, msg: msg)
} catch {
print(error)
throw error
//guard var json_cstr = json.cString(using: .utf8) else { return nil }
//json_cs
var tce = ndb_tce()
let len = ndb_ws_event_from_json(cstr, Int32(json.utf8.count), &tce, data, Int32(bufsize))
if len <= 0 {
free(data)
return nil
}
switch tce.evtype {
case NDB_TCE_OK:
defer { free(data) }
guard let evid_str = sized_cstr(cstr: tce.subid, len: tce.subid_len),
let evid = hex_decode_noteid(evid_str),
let msg = sized_cstr(cstr: tce.command_result.msg, len: tce.command_result.msglen) else {
return nil
}
let cr = CommandResult(event_id: evid, ok: tce.command_result.ok == 1, msg: msg)
return .ok(cr)
case NDB_TCE_EOSE:
defer { free(data) }
guard let subid = sized_cstr(cstr: tce.subid, len: tce.subid_len) else {
return nil
}
return .eose(subid)
case NDB_TCE_EVENT:
// Create new Data with just the valid bytes
guard let note_data = realloc(data, Int(len)) else {
free(data)
return nil
}
let new_note = note_data.assumingMemoryBound(to: ndb_note.self)
let note = NdbNote(note: new_note, owned_size: Int(len))
guard let subid = sized_cstr(cstr: tce.subid, len: tce.subid_len) else {
free(data)
return nil
}
return .event(subid, note)
case NDB_TCE_NOTICE:
free(data)
return .notice("")
default:
free(data)
return nil
}
self = .ok(cr)
return
//ev.pow = count_hash_leading_zero_bits(ev.id)
}
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "expected EVENT, NOTICE or OK, got \(typ)"))
}
}
func sized_cstr(cstr: UnsafePointer<CChar>, len: Int32) -> String? {
let msgbuf = Data(bytes: cstr, count: Int(len))
return String(data: msgbuf, encoding: .utf8)
}