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:
62
damus/Types/Ids/IdType.swift
Normal file
62
damus/Types/Ids/IdType.swift
Normal 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))
|
||||
}
|
||||
|
||||
52
damus/Types/Ids/NoteId.swift
Normal file
52
damus/Types/Ids/NoteId.swift
Normal 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
|
||||
}
|
||||
}
|
||||
48
damus/Types/Ids/Pubkey.swift
Normal file
48
damus/Types/Ids/Pubkey.swift
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
91
damus/Types/Ids/Referenced.swift
Normal file
91
damus/Types/Ids/Referenced.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user