damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

NdbTagElem.swift (4146B)


      1 //
      2 //  NdbTagElem.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2023-07-21.
      6 //
      7 
      8 import Foundation
      9 
     10 struct NdbStrIter: IteratorProtocol {
     11     typealias Element = CChar
     12 
     13     var ind: Int
     14     let str: ndb_str
     15     let tag: NdbTagElem // stored for lifetime reasons
     16 
     17     mutating func next() -> CChar? {
     18         let c = str.str[ind]
     19         if (c != 0) {
     20             ind += 1
     21             return c
     22         }
     23 
     24         return nil
     25     }
     26 
     27     init(tag: NdbTagElem) {
     28         self.str = ndb_tag_str(tag.note.note, tag.tag, tag.index)
     29         self.ind = 0
     30         self.tag = tag
     31     }
     32 }
     33 
     34 struct NdbTagElem: Sequence, Hashable, Equatable {
     35     let note: NdbNote
     36     let tag: UnsafeMutablePointer<ndb_tag>
     37     let index: Int32
     38     let str: ndb_str
     39 
     40     func hash(into hasher: inout Hasher) {
     41         if str.flag == NDB_PACKED_ID {
     42             hasher.combine(bytes: UnsafeRawBufferPointer(start: str.id, count: 32))
     43         } else {
     44             hasher.combine(bytes: UnsafeRawBufferPointer(start: str.str, count: strlen(str.str)))
     45         }
     46     }
     47 
     48     static func == (lhs: NdbTagElem, rhs: NdbTagElem) -> Bool {
     49         if lhs.str.flag == NDB_PACKED_ID && rhs.str.flag == NDB_PACKED_ID {
     50             return memcmp(lhs.str.id, rhs.str.id, 32) == 0
     51         } else if lhs.str.flag == NDB_PACKED_ID || rhs.str.flag == NDB_PACKED_ID {
     52             return false
     53         }
     54 
     55         let l = strlen(lhs.str.str)
     56         let r = strlen(rhs.str.str)
     57         if l != r { return false }
     58 
     59         return memcmp(lhs.str.str, rhs.str.str, r) == 0
     60     }
     61 
     62     init(note: NdbNote, tag: UnsafeMutablePointer<ndb_tag>, index: Int32) {
     63         self.note = note
     64         self.tag = tag
     65         self.index = index
     66         self.str = ndb_tag_str(note.note, tag, index)
     67     }
     68 
     69     var is_id: Bool {
     70         return str.flag == NDB_PACKED_ID
     71     }
     72 
     73     var isEmpty: Bool {
     74         if str.flag == NDB_PACKED_ID {
     75             return false
     76         }
     77         return str.str[0] == 0
     78     }
     79 
     80     var count: Int {
     81         if str.flag == NDB_PACKED_ID {
     82             return 32
     83         } else {
     84             return strlen(str.str)
     85         }
     86     }
     87 
     88     var single_char: AsciiCharacter? {
     89         let c = str.str[0]
     90         guard c != 0 && str.str[1] == 0 else { return nil }
     91         return AsciiCharacter(c)
     92     }
     93 
     94     func matches_char(_ c: AsciiCharacter) -> Bool {
     95         return str.str[0] == c.cchar && str.str[1] == 0
     96     }
     97 
     98     func matches_id(_ d: Data) -> Bool {
     99         if str.flag == NDB_PACKED_ID, d.count == 32 {
    100             return memcmp(d.bytes, str.id, 32) == 0
    101         }
    102         return false
    103     }
    104 
    105     func matches_str(_ s: String, tag_len: Int? = nil) -> Bool {
    106         if str.flag == NDB_PACKED_ID,
    107            s.utf8.count == 64,
    108            var decoded = hex_decode(s), decoded.count == 32
    109         {
    110             return memcmp(&decoded, str.id, 32) == 0
    111         }
    112 
    113         // Ensure the Swift string's utf8 count matches the C string's length.
    114         guard (tag_len ?? strlen(str.str)) == s.utf8.count else {
    115             return false
    116         }
    117 
    118         // Compare directly using the utf8 view.
    119         return s.utf8.withContiguousStorageIfAvailable { buffer in
    120             memcmp(buffer.baseAddress, str.str, buffer.count) == 0
    121         } ?? false
    122     }
    123 
    124     func data() -> NdbData {
    125         return NdbData(note: note, str: self.str)
    126     }
    127 
    128     func id() -> Data? {
    129         guard case .id(let id) = self.data() else { return nil }
    130         return id.id
    131     }
    132 
    133     func u64() -> UInt64? {
    134         switch self.data() {
    135         case .id:
    136             return nil
    137         case .str(let str):
    138             var end_ptr = UnsafeMutablePointer<CChar>(nil as OpaquePointer?)
    139             let res = strtoull(str.str, &end_ptr, 10)
    140 
    141             if end_ptr?.pointee == 0 {
    142                 return res
    143             } else {
    144                 return nil
    145             }
    146         }
    147     }
    148 
    149     func string() -> String {
    150         switch self.data() {
    151         case .id(let id):
    152             return hex_encode(id.id)
    153         case .str(let s):
    154             return String(cString: s.str, encoding: .utf8) ?? ""
    155         }
    156     }
    157 
    158     func makeIterator() -> NdbStrIter {
    159         return NdbStrIter(tag: self)
    160     }
    161 }
    162