mirror of
git://jb55.com/damus
synced 2024-09-05 21:03:51 +00:00
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
147 lines
3.7 KiB
Swift
147 lines
3.7 KiB
Swift
//
|
|
// NdbTagElem.swift
|
|
// damus
|
|
//
|
|
// Created by William Casarin on 2023-07-21.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
struct NdbStrIter: IteratorProtocol {
|
|
typealias Element = CChar
|
|
|
|
var ind: Int
|
|
let str: ndb_str
|
|
let tag: NdbTagElem // stored for lifetime reasons
|
|
|
|
mutating func next() -> CChar? {
|
|
let c = str.str[ind]
|
|
if (c != 0) {
|
|
ind += 1
|
|
return c
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
init(tag: NdbTagElem) {
|
|
self.str = ndb_tag_str(tag.note.note, tag.tag, tag.index)
|
|
self.ind = 0
|
|
self.tag = tag
|
|
}
|
|
}
|
|
|
|
struct NdbTagElem: Sequence, Hashable, Equatable {
|
|
let note: NdbNote
|
|
let tag: UnsafeMutablePointer<ndb_tag>
|
|
let index: Int32
|
|
let str: ndb_str
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
if str.flag == NDB_PACKED_ID {
|
|
hasher.combine(bytes: UnsafeRawBufferPointer(start: str.id, count: 32))
|
|
} else {
|
|
hasher.combine(bytes: UnsafeRawBufferPointer(start: str.str, count: strlen(str.str)))
|
|
}
|
|
}
|
|
|
|
static func == (lhs: NdbTagElem, rhs: NdbTagElem) -> Bool {
|
|
if lhs.str.flag == NDB_PACKED_ID && rhs.str.flag == NDB_PACKED_ID {
|
|
return memcmp(lhs.str.id, rhs.str.id, 32) == 0
|
|
} else if lhs.str.flag == NDB_PACKED_ID || rhs.str.flag == NDB_PACKED_ID {
|
|
return false
|
|
}
|
|
|
|
let l = strlen(lhs.str.str)
|
|
let r = strlen(rhs.str.str)
|
|
if l != r { return false }
|
|
|
|
return memcmp(lhs.str.str, rhs.str.str, r) == 0
|
|
}
|
|
|
|
init(note: NdbNote, tag: UnsafeMutablePointer<ndb_tag>, index: Int32) {
|
|
self.note = note
|
|
self.tag = tag
|
|
self.index = index
|
|
self.str = ndb_tag_str(note.note, tag, index)
|
|
}
|
|
|
|
var is_id: Bool {
|
|
return str.flag == NDB_PACKED_ID
|
|
}
|
|
|
|
var isEmpty: Bool {
|
|
if str.flag == NDB_PACKED_ID {
|
|
return false
|
|
}
|
|
return str.str[0] == 0
|
|
}
|
|
|
|
var count: Int {
|
|
if str.flag == NDB_PACKED_ID {
|
|
return 32
|
|
} else {
|
|
return strlen(str.str)
|
|
}
|
|
}
|
|
|
|
var single_char: AsciiCharacter? {
|
|
let c = str.str[0]
|
|
guard c != 0 && str.str[1] == 0 else { return nil }
|
|
return AsciiCharacter(c)
|
|
}
|
|
|
|
func matches_char(_ c: AsciiCharacter) -> Bool {
|
|
return str.str[0] == c.cchar && str.str[1] == 0
|
|
}
|
|
|
|
func matches_id(_ d: Data) -> Bool {
|
|
if str.flag == NDB_PACKED_ID, d.count == 32 {
|
|
return memcmp(d.bytes, str.id, 32) == 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
func matches_str(_ s: String, tag_len: Int? = nil) -> Bool {
|
|
if str.flag == NDB_PACKED_ID,
|
|
s.utf8.count == 64,
|
|
var decoded = hex_decode(s), decoded.count == 32
|
|
{
|
|
return memcmp(&decoded, str.id, 32) == 0
|
|
}
|
|
|
|
// Ensure the Swift string's utf8 count matches the C string's length.
|
|
guard (tag_len ?? strlen(str.str)) == s.utf8.count else {
|
|
return false
|
|
}
|
|
|
|
// Compare directly using the utf8 view.
|
|
return s.utf8.withContiguousStorageIfAvailable { buffer in
|
|
memcmp(buffer.baseAddress, str.str, buffer.count) == 0
|
|
} ?? false
|
|
}
|
|
|
|
func data() -> NdbData {
|
|
return NdbData(note: note, str: self.str)
|
|
}
|
|
|
|
func id() -> Data? {
|
|
guard case .id(let id) = self.data() else { return nil }
|
|
return id.id
|
|
}
|
|
|
|
func string() -> String {
|
|
switch self.data() {
|
|
case .id(let id):
|
|
return hex_encode(id.id)
|
|
case .str(let s):
|
|
return String(cString: s.str, encoding: .utf8) ?? ""
|
|
}
|
|
}
|
|
|
|
func makeIterator() -> NdbStrIter {
|
|
return NdbStrIter(tag: self)
|
|
}
|
|
}
|
|
|