mirror of
git://jb55.com/damus
synced 2024-09-18 19:23:49 +00:00
ndb: implement eventref building from ndb notes
This commit is contained in:
parent
c8e236b6d5
commit
1e9e4a7f3a
@ -73,3 +73,70 @@ func parse_note_content_ndb(note: NdbNote) -> Blocks {
|
|||||||
return Blocks(words: words, blocks: out)
|
return Blocks(words: words, blocks: out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func interpret_event_refs_ndb(blocks: [Block], tags: TagsSequence) -> [EventRef] {
|
||||||
|
if tags.count == 0 {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
/// build a set of indices for each event mention
|
||||||
|
let mention_indices = build_mention_indices(blocks, type: .event)
|
||||||
|
|
||||||
|
/// simpler case with no mentions
|
||||||
|
if mention_indices.count == 0 {
|
||||||
|
let ev_refs = References(tags: tags).ids()
|
||||||
|
return interp_event_refs_without_mentions_ndb(ev_refs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interp_event_refs_with_mentions_ndb(tags: tags, mention_indices: mention_indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func interp_event_refs_without_mentions_ndb(_ ev_tags: LazyFilterSequence<References>) -> [EventRef] {
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
var evrefs: [EventRef] = []
|
||||||
|
var first: Bool = true
|
||||||
|
var first_ref: Reference? = nil
|
||||||
|
|
||||||
|
for ref in ev_tags {
|
||||||
|
if first {
|
||||||
|
first_ref = ref
|
||||||
|
evrefs.append(.thread_id(ref.to_referenced_id()))
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
|
||||||
|
evrefs.append(.reply(ref.to_referenced_id()))
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if let first_ref, count == 1 {
|
||||||
|
let r = first_ref.to_referenced_id()
|
||||||
|
return [.reply_to_root(r)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return evrefs
|
||||||
|
}
|
||||||
|
|
||||||
|
func interp_event_refs_with_mentions_ndb(tags: TagsSequence, mention_indices: Set<Int>) -> [EventRef] {
|
||||||
|
var mentions: [EventRef] = []
|
||||||
|
var ev_refs: [ReferencedId] = []
|
||||||
|
var i: Int = 0
|
||||||
|
|
||||||
|
for tag in tags {
|
||||||
|
if tag.count >= 2, let t = tag[0], t.matches_char("e"),
|
||||||
|
let ref = tag_to_refid_ndb(tag)
|
||||||
|
{
|
||||||
|
if mention_indices.contains(i) {
|
||||||
|
let mention = Mention(index: i, type: .event, ref: ref)
|
||||||
|
mentions.append(.mention(mention))
|
||||||
|
} else {
|
||||||
|
ev_refs.append(ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var replies = interp_event_refs_without_mentions(ev_refs)
|
||||||
|
replies.append(contentsOf: mentions)
|
||||||
|
return replies
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum EventRef {
|
enum EventRef: Equatable {
|
||||||
case mention(Mention)
|
case mention(Mention)
|
||||||
case thread_id(ReferencedId)
|
case thread_id(ReferencedId)
|
||||||
case reply(ReferencedId)
|
case reply(ReferencedId)
|
||||||
|
@ -158,7 +158,7 @@ func render_blocks(blocks: [Block]) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Blocks {
|
struct Blocks: Equatable {
|
||||||
let words: Int
|
let words: Int
|
||||||
let blocks: [Block]
|
let blocks: [Block]
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@ import Foundation
|
|||||||
struct Reference {
|
struct Reference {
|
||||||
let key: AsciiCharacter
|
let key: AsciiCharacter
|
||||||
let id: NdbTagElem
|
let id: NdbTagElem
|
||||||
|
var ref_id: NdbTagElem {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
func to_referenced_id() -> ReferencedId {
|
func to_referenced_id() -> ReferencedId {
|
||||||
ReferencedId(ref_id: id.string(), relay_id: nil, key: key.string)
|
ReferencedId(ref_id: id.string(), relay_id: nil, key: key.string)
|
||||||
@ -17,11 +20,11 @@ struct Reference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct References: Sequence, IteratorProtocol {
|
struct References: Sequence, IteratorProtocol {
|
||||||
let note: NdbNote
|
let tags: TagsSequence
|
||||||
var tags: TagsIterator
|
var tags_iter: TagsIterator
|
||||||
|
|
||||||
mutating func next() -> Reference? {
|
mutating func next() -> Reference? {
|
||||||
while let tag = tags.next() {
|
while let tag = tags_iter.next() {
|
||||||
guard let key = tag[0], key.count == 1,
|
guard let key = tag[0], key.count == 1,
|
||||||
let id = tag[1], id.is_id
|
let id = tag[1], id.is_id
|
||||||
else { continue }
|
else { continue }
|
||||||
@ -35,9 +38,20 @@ struct References: Sequence, IteratorProtocol {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
init(note: NdbNote) {
|
|
||||||
self.note = note
|
func ids() -> LazyFilterSequence<References> {
|
||||||
self.tags = note.tags().makeIterator()
|
References(tags: tags).lazy
|
||||||
|
.filter() { ref in ref.key == "e" }
|
||||||
|
}
|
||||||
|
|
||||||
|
func pubkeys() -> LazyFilterSequence<References> {
|
||||||
|
References(tags: tags).lazy
|
||||||
|
.filter() { ref in ref.key == "p" }
|
||||||
|
}
|
||||||
|
|
||||||
|
init(tags: TagsSequence) {
|
||||||
|
self.tags = tags
|
||||||
|
self.tags_iter = tags.makeIterator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -39,6 +39,14 @@ class NdbNote {
|
|||||||
let count: Int
|
let count: Int
|
||||||
let note: UnsafeMutablePointer<ndb_note>
|
let note: UnsafeMutablePointer<ndb_note>
|
||||||
|
|
||||||
|
// cached stuff (TODO: remove these)
|
||||||
|
private var _event_refs: [EventRef]? = nil
|
||||||
|
var decrypted_content: String? = nil
|
||||||
|
private var _blocks: Blocks? = nil
|
||||||
|
private lazy var inner_event: NdbNote? = {
|
||||||
|
return NdbNote.owned_from_json_cstr(json: content_raw, json_len: content_len)
|
||||||
|
}()
|
||||||
|
|
||||||
init(note: UnsafeMutablePointer<ndb_note>, owned_size: Int?) {
|
init(note: UnsafeMutablePointer<ndb_note>, owned_size: Int?) {
|
||||||
self.note = note
|
self.note = note
|
||||||
self.owned = owned_size != nil
|
self.owned = owned_size != nil
|
||||||
@ -86,12 +94,20 @@ class NdbNote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func owned_from_json(json: String, bufsize: Int = 2 << 18) -> NdbNote? {
|
static func owned_from_json(json: String, bufsize: Int = 2 << 18) -> NdbNote? {
|
||||||
|
return json.withCString { cstr in
|
||||||
|
return NdbNote.owned_from_json_cstr(
|
||||||
|
json: cstr, json_len: UInt32(json.utf8.count), bufsize: bufsize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func owned_from_json_cstr(json: UnsafePointer<CChar>, json_len: UInt32, bufsize: Int = 2 << 18) -> NdbNote? {
|
||||||
let data = malloc(bufsize)
|
let data = malloc(bufsize)
|
||||||
guard var json_cstr = json.cString(using: .utf8) else { return nil }
|
//guard var json_cstr = json.cString(using: .utf8) else { return nil }
|
||||||
|
|
||||||
|
//json_cs
|
||||||
var note: UnsafeMutablePointer<ndb_note>?
|
var note: UnsafeMutablePointer<ndb_note>?
|
||||||
|
|
||||||
let len = ndb_note_from_json(&json_cstr, Int32(json_cstr.count), ¬e, data, Int32(bufsize))
|
let len = ndb_note_from_json(json, Int32(json_len), ¬e, data, Int32(bufsize))
|
||||||
|
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
free(data)
|
free(data)
|
||||||
@ -100,8 +116,8 @@ class NdbNote {
|
|||||||
|
|
||||||
// Create new Data with just the valid bytes
|
// Create new Data with just the valid bytes
|
||||||
guard let note_data = realloc(data, Int(len)) else { return nil }
|
guard let note_data = realloc(data, Int(len)) else { return nil }
|
||||||
|
|
||||||
let new_note = note_data.assumingMemoryBound(to: ndb_note.self)
|
let new_note = note_data.assumingMemoryBound(to: ndb_note.self)
|
||||||
|
|
||||||
return NdbNote(note: new_note, owned_size: Int(len))
|
return NdbNote(note: new_note, owned_size: Int(len))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,26 +168,39 @@ extension NdbNote {
|
|||||||
|
|
||||||
// TODO: References iterator
|
// TODO: References iterator
|
||||||
public var referenced_ids: LazyFilterSequence<References> {
|
public var referenced_ids: LazyFilterSequence<References> {
|
||||||
References(note: self).lazy
|
References(tags: self.tags()).ids()
|
||||||
.filter() { ref in ref.key == "e" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var referenced_pubkeys: LazyFilterSequence<References> {
|
public var referenced_pubkeys: LazyFilterSequence<References> {
|
||||||
References(note: self).lazy
|
References(tags: self.tags()).pubkeys()
|
||||||
.filter() { ref in ref.key == "p" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
func event_refs(_ privkey: String?) -> [EventRef] {
|
func event_refs(_ privkey: String?) -> [EventRef] {
|
||||||
if let rs = _event_refs {
|
if let rs = _event_refs {
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
let refs = interpret_event_refs(blocks: self.blocks(privkey).blocks, tags: self.tags)
|
let refs = interpret_event_refs_ndb(blocks: self.blocks(privkey).blocks, tags: self.tags())
|
||||||
self._event_refs = refs
|
self._event_refs = refs
|
||||||
return refs
|
return refs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func get_content(_ privkey: String?) -> String {
|
||||||
|
if known_kind == .dm {
|
||||||
|
return decrypted(privkey: privkey) ?? "*failed to decrypt content*"
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
func blocks(_ privkey: String?) -> Blocks {
|
||||||
|
if let bs = _blocks { return bs }
|
||||||
|
|
||||||
|
let blocks = get_blocks(content: self.get_content(privkey))
|
||||||
|
self._blocks = blocks
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NDBTODO: switch this to operating on bytes not strings
|
||||||
func decrypted(privkey: String?) -> String? {
|
func decrypted(privkey: String?) -> String? {
|
||||||
if let decrypted_content = decrypted_content {
|
if let decrypted_content = decrypted_content {
|
||||||
return decrypted_content
|
return decrypted_content
|
||||||
@ -185,38 +214,32 @@ extension NdbNote {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubkey = self.pubkey
|
// NDBTODO: don't hex encode
|
||||||
|
var pubkey = hex_encode(self.pubkey)
|
||||||
// This is our DM, we need to use the pubkey of the person we're talking to instead
|
// This is our DM, we need to use the pubkey of the person we're talking to instead
|
||||||
|
|
||||||
if our_pubkey == pubkey {
|
if our_pubkey == pubkey {
|
||||||
guard let refkey = self.referenced_pubkeys.first else {
|
guard let refkey = self.referenced_pubkeys.first else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey = refkey.ref_id
|
pubkey = refkey.ref_id.string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NDBTODO: pass data to pubkey
|
||||||
let dec = decrypt_dm(key, pubkey: pubkey, content: self.content, encoding: .base64)
|
let dec = decrypt_dm(key, pubkey: pubkey, content: self.content, encoding: .base64)
|
||||||
self.decrypted_content = dec
|
self.decrypted_content = dec
|
||||||
|
|
||||||
return dec
|
return dec
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_content(_ privkey: String?) -> String {
|
|
||||||
if known_kind == .dm {
|
|
||||||
return decrypted(privkey: privkey) ?? "*failed to decrypt content*"
|
|
||||||
}
|
|
||||||
|
|
||||||
return content
|
/*
|
||||||
}
|
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
return "NostrEvent { id: \(id) pubkey \(pubkey) kind \(kind) tags \(tags) content '\(content)' }"
|
return "NostrEvent { id: \(id) pubkey \(pubkey) kind \(kind) tags \(tags) content '\(content)' }"
|
||||||
}
|
}
|
||||||
|
|
||||||
var known_kind: NostrKind? {
|
|
||||||
return NostrKind.init(rawValue: kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id, sig, tags, pubkey, created_at, kind, content
|
case id, sig, tags, pubkey, created_at, kind, content
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,10 @@ struct TagsSequence: Sequence {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func references() -> References {
|
||||||
|
return References(tags: self)
|
||||||
|
}
|
||||||
|
|
||||||
func makeIterator() -> TagsIterator {
|
func makeIterator() -> TagsIterator {
|
||||||
return .init(note: note)
|
return .init(note: note)
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,72 @@ final class NdbTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_perf_old_decoding() {
|
||||||
|
self.measure {
|
||||||
|
let event = decode_nostr_event_json(test_contact_list_json)
|
||||||
|
XCTAssertNotNil(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_perf_old_iter() {
|
||||||
|
self.measure {
|
||||||
|
let event = decode_nostr_event_json(test_contact_list_json)
|
||||||
|
XCTAssertNotNil(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func longer_iter(_ n: Int = 1000) -> XCTMeasureOptions {
|
||||||
|
let opts = XCTMeasureOptions()
|
||||||
|
opts.iterationCount = n
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_perf_interp_evrefs_old() {
|
||||||
|
guard let event = decode_nostr_event_json(test_reply_json) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.measure(options: longer_iter()) {
|
||||||
|
let blocks = event.blocks(nil).blocks
|
||||||
|
let xs = interpret_event_refs(blocks: blocks, tags: event.tags)
|
||||||
|
XCTAssertEqual(xs.count, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_perf_interp_evrefs_ndb() {
|
||||||
|
guard let note = NdbNote.owned_from_json(json: test_reply_json) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.measure(options: longer_iter()) {
|
||||||
|
let blocks = note.blocks(nil).blocks
|
||||||
|
let xs = interpret_event_refs_ndb(blocks: blocks, tags: note.tags())
|
||||||
|
XCTAssertEqual(xs.count, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_decoded_events_are_equal() {
|
||||||
|
let event = decode_nostr_event_json(test_reply_json)
|
||||||
|
let note = NdbNote.owned_from_json(json: test_reply_json)
|
||||||
|
|
||||||
|
XCTAssertNotNil(note)
|
||||||
|
XCTAssertNotNil(event)
|
||||||
|
guard let note else { return }
|
||||||
|
guard let event else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(note.content_len, UInt32(event.content.utf8.count))
|
||||||
|
XCTAssertEqual(hex_encode(note.pubkey), event.pubkey)
|
||||||
|
XCTAssertEqual(hex_encode(note.id), event.id)
|
||||||
|
|
||||||
|
let ev_blocks = event.blocks(nil)
|
||||||
|
let note_blocks = note.blocks(nil)
|
||||||
|
|
||||||
|
XCTAssertEqual(ev_blocks, note_blocks)
|
||||||
|
|
||||||
|
let event_refs = interpret_event_refs(blocks: ev_blocks.blocks, tags: event.tags)
|
||||||
|
let note_refs = interpret_event_refs_ndb(blocks: note_blocks.blocks, tags: note.tags())
|
||||||
|
|
||||||
|
XCTAssertEqual(event_refs, note_refs)
|
||||||
|
}
|
||||||
|
|
||||||
func test_iteration_perf() throws {
|
func test_iteration_perf() throws {
|
||||||
guard let note = NdbNote.owned_from_json(json: test_contact_list_json) else {
|
guard let note = NdbNote.owned_from_json(json: test_contact_list_json) else {
|
||||||
XCTAssert(false)
|
XCTAssert(false)
|
||||||
|
Loading…
Reference in New Issue
Block a user