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