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

View File

@@ -0,0 +1,62 @@
//
// IdType.swift
// damus
//
// Created by William Casarin on 2023-07-28.
//
import Foundation
protocol IdType: Codable, CustomStringConvertible, Hashable, Equatable {
var id: Data { get }
init(_ data: Data)
init(from decoder: Decoder) throws
func encode(to encoder: Encoder) throws
}
extension IdType {
func hex() -> String {
hex_encode(self.id)
}
var bytes: [UInt8] {
self.id.bytes
}
static var empty: Self {
return Self.init(Data(repeating: 0, count: 32))
}
var description: String {
self.hex()
}
init(from decoder: Decoder) throws {
self.init(try hex_decoder(decoder))
}
func encode(to encoder: Encoder) throws {
try hex_encoder(to: encoder, data: self.id)
}
}
func hex_decoder(_ decoder: Decoder, expected_len: Int = 32) throws -> Data {
let container = try decoder.singleValueContainer()
guard let arr = hex_decode(try container.decode(String.self)) else {
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "hex string"))
}
if arr.count != expected_len {
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "too long"))
}
return Data(bytes: arr, count: arr.count)
}
func hex_encoder(to encoder: Encoder, data: Data) throws {
var container = encoder.singleValueContainer()
try container.encode(hex_encode(data))
}

View File

@@ -0,0 +1,52 @@
//
// NoteId.swift
// damus
//
// Created by William Casarin on 2023-07-28.
//
import Foundation
struct NoteId: IdType, TagKey, TagConvertible {
let id: Data
init(_ data: Data) {
self.id = data
}
init?(hex: String) {
guard let note_id = hex_decode_noteid(hex) else {
return nil
}
self = note_id
}
var bech32: String {
bech32_note_id(self)
}
/// Refer to this NoteId as a QuoteId
var quote_id: QuoteId {
QuoteId(self.id)
}
var keychar: AsciiCharacter { "e" }
var tag: [String] {
["e", self.hex()]
}
static func from_tag(tag: TagSequence) -> NoteId? {
var i = tag.makeIterator()
guard tag.count >= 2,
let t0 = i.next(),
let key = t0.single_char,
key == "e",
let t1 = i.next(),
let note_id = t1.id().map(NoteId.init)
else { return nil }
return note_id
}
}

View File

@@ -0,0 +1,48 @@
//
// Pubkey.swift
// damus
//
// Created by William Casarin on 2023-07-28.
//
import Foundation
struct Pubkey: IdType, TagKey, TagConvertible, Identifiable {
let id: Data
var tag: [String] {
[keychar.description, self.hex()]
}
init?(hex: String) {
guard let id = hex_decode_pubkey(hex) else {
return nil
}
self = id
}
init(_ data: Data) {
self.id = data
}
var npub: String {
bech32_pubkey(self)
}
var keychar: AsciiCharacter { "p" }
static func from_tag(tag: TagSequence) -> Pubkey? {
var i = tag.makeIterator()
guard tag.count >= 2,
let t0 = i.next(),
let key = t0.single_char,
key == "p",
let t1 = i.next(),
let pubkey = t1.id().map(Pubkey.init)
else { return nil }
return pubkey
}
}

View File

@@ -0,0 +1,91 @@
//
// Referenced.swift
// damus
//
// Created by William Casarin on 2023-07-28.
//
import Foundation
enum Marker: String {
case root
case reply
case mention
init?(_ tag: TagElem) {
let len = tag.count
if len == 4, tag.matches_str("root", tag_len: len) {
self = .root
} else if len == 5, tag.matches_str("reply", tag_len: len) {
self = .reply
} else if len == 7, tag.matches_str("mention", tag_len: len) {
self = .mention
} else {
return nil
}
}
}
struct NoteRef: IdType, TagConvertible, Equatable {
let note_id: NoteId
let relay: String?
let marker: Marker?
var id: Data {
self.note_id.id
}
init(note_id: NoteId, relay: String? = nil, marker: Marker? = nil) {
self.note_id = note_id
self.relay = relay
self.marker = marker
}
static func note_id(_ note_id: NoteId) -> NoteRef {
return NoteRef(note_id: note_id)
}
init(_ data: Data) {
self.note_id = NoteId(data)
self.relay = nil
self.marker = nil
}
var tag: [String] {
var t = ["e", self.hex()]
if let marker {
t.append(relay ?? "")
t.append(marker.rawValue)
} else if let relay {
t.append(relay)
}
return t
}
static func from_tag(tag: TagSequence) -> NoteRef? {
guard tag.count >= 2 else { return nil }
var i = tag.makeIterator()
guard let t0 = i.next(),
t0.single_char == "e",
let t1 = i.next(),
let note_id = t1.id().map(NoteId.init)
else {
return nil
}
var relay: String? = nil
var marker: Marker? = nil
if tag.count >= 3, let r = i.next() {
relay = r.string()
if tag.count >= 4, let m = i.next() {
marker = Marker(m)
}
}
return NoteRef(note_id: note_id, relay: relay, marker: marker)
}
}