damus

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

commit 61051ee853410c0e26be9441bcce2f446d39ba19
parent dc7826c4e59dfd50901ac2aad5101e8c994b2c90
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 21 Jul 2023 14:54:03 -0700

nostrdb: add initial swift integration

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 49++++++++++++++++++++++++++++++++++++++++++++++++-
AdamusTests/NDB/NdbTests.swift | 48++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/AsciiCharacter.swift | 35+++++++++++++++++++++++++++++++++++
Anostrdb/NdbNote.swift | 42++++++++++++++++++++++++++++++++++++++++++
Anostrdb/NdbTagElem.swift | 29+++++++++++++++++++++++++++++
Anostrdb/NdbTagIterator.swift | 47+++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/NdbTagsIterator.swift | 40++++++++++++++++++++++++++++++++++++++++
7 files changed, 289 insertions(+), 1 deletion(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -134,6 +134,9 @@ 4C54AA0C29A5543C003E4487 /* ZapGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0B29A5543C003E4487 /* ZapGroup.swift */; }; 4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */; }; 4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */; }; + 4C5D5C992A6AF8F80024563C /* NdbNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90548A2A6AEDEE00811EEC /* NdbNote.swift */; }; + 4C5D5C9A2A6AF8F80024563C /* NdbTagIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9054882A6AED4700811EEC /* NdbTagIterator.swift */; }; + 4C5D5C9D2A6B2CB40024563C /* AsciiCharacter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5D5C9C2A6B2CB40024563C /* AsciiCharacter.swift */; }; 4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5F9113283D694D0052CD1C /* FollowTarget.swift */; }; 4C5F9116283D855D0052CD1C /* EventsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5F9115283D855D0052CD1C /* EventsModel.swift */; }; 4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5F9117283D88E40052CD1C /* FollowingModel.swift */; }; @@ -180,6 +183,7 @@ 4C8D1A6C29F1DFC200ACDF75 /* FriendIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8D1A6B29F1DFC200ACDF75 /* FriendIcon.swift */; }; 4C8D1A6F29F31E5000ACDF75 /* FriendsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8D1A6E29F31E5000ACDF75 /* FriendsButton.swift */; }; 4C8EC52529D1FA6C0085D9A8 /* DamusColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8EC52429D1FA6C0085D9A8 /* DamusColors.swift */; }; + 4C9054852A6AEAA000811EEC /* NdbTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9054842A6AEAA000811EEC /* NdbTests.swift */; }; 4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD17283A9EE5008EE7EF /* LoginView.swift */; }; 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD19283AA67F008EE7EF /* Bech32.swift */; }; 4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; }; @@ -246,6 +250,8 @@ 4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; }; 4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128929E9D10C0006FA5A /* SignalView.swift */; }; 4CDA128C29EB19C40006FA5A /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128B29EB19C40006FA5A /* LocalNotification.swift */; }; + 4CDD1AE02A6B305F001CD4DF /* NdbTagElem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDD1ADF2A6B305F001CD4DF /* NdbTagElem.swift */; }; + 4CDD1AE22A6B3074001CD4DF /* NdbTagsIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDD1AE12A6B3074001CD4DF /* NdbTagsIterator.swift */; }; 4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */; }; 4CE0E2B629A3ED5500DB4CA2 /* InnerTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */; }; 4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE1398F29F0661A00AC6A0B /* RepostAction.swift */; }; @@ -610,6 +616,7 @@ 4C54AA0B29A5543C003E4487 /* ZapGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapGroup.swift; sourceTree = "<group>"; }; 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHomeModel.swift; sourceTree = "<group>"; }; 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsView.swift; sourceTree = "<group>"; }; + 4C5D5C9C2A6B2CB40024563C /* AsciiCharacter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsciiCharacter.swift; sourceTree = "<group>"; }; 4C5F9113283D694D0052CD1C /* FollowTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowTarget.swift; sourceTree = "<group>"; }; 4C5F9115283D855D0052CD1C /* EventsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsModel.swift; sourceTree = "<group>"; }; 4C5F9117283D88E40052CD1C /* FollowingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingModel.swift; sourceTree = "<group>"; }; @@ -660,7 +667,9 @@ 4C8D1A6B29F1DFC200ACDF75 /* FriendIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendIcon.swift; sourceTree = "<group>"; }; 4C8D1A6E29F31E5000ACDF75 /* FriendsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendsButton.swift; sourceTree = "<group>"; }; 4C8EC52429D1FA6C0085D9A8 /* DamusColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusColors.swift; sourceTree = "<group>"; }; - 4C9054802A6A065700811EEC /* jsmn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = jsmn.h; sourceTree = "<group>"; }; + 4C9054842A6AEAA000811EEC /* NdbTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbTests.swift; sourceTree = "<group>"; }; + 4C9054882A6AED4700811EEC /* NdbTagIterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbTagIterator.swift; sourceTree = "<group>"; }; + 4C90548A2A6AEDEE00811EEC /* NdbNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbNote.swift; sourceTree = "<group>"; }; 4C90BD17283A9EE5008EE7EF /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; }; 4C90BD19283AA67F008EE7EF /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = "<group>"; }; 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; }; @@ -731,6 +740,8 @@ 4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; }; 4CDA128929E9D10C0006FA5A /* SignalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalView.swift; sourceTree = "<group>"; }; 4CDA128B29EB19C40006FA5A /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; }; + 4CDD1ADF2A6B305F001CD4DF /* NdbTagElem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbTagElem.swift; sourceTree = "<group>"; }; + 4CDD1AE12A6B3074001CD4DF /* NdbTagsIterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbTagsIterator.swift; sourceTree = "<group>"; }; 4CDD1AE72A6B3611001CD4DF /* jsmn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = jsmn.h; sourceTree = "<group>"; }; 4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventHolder.swift; sourceTree = "<group>"; }; 4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerTimelineView.swift; sourceTree = "<group>"; }; @@ -1298,6 +1309,34 @@ path = Buttons; sourceTree = "<group>"; }; + 4C9054832A6AEA7B00811EEC /* NDB */ = { + isa = PBXGroup; + children = ( + 4C9054842A6AEAA000811EEC /* NdbTests.swift */, + ); + path = NDB; + sourceTree = "<group>"; + }; + 4C9054862A6AEB4500811EEC /* nostrdb */ = { + isa = PBXGroup; + children = ( + 4C9054892A6AEDCD00811EEC /* Tests */, + 4C9054882A6AED4700811EEC /* NdbTagIterator.swift */, + 4C90548A2A6AEDEE00811EEC /* NdbNote.swift */, + 4C5D5C9C2A6B2CB40024563C /* AsciiCharacter.swift */, + 4CDD1ADF2A6B305F001CD4DF /* NdbTagElem.swift */, + 4CDD1AE12A6B3074001CD4DF /* NdbTagsIterator.swift */, + ); + path = nostrdb; + sourceTree = "<group>"; + }; + 4C9054892A6AEDCD00811EEC /* Tests */ = { + isa = PBXGroup; + children = ( + ); + path = Tests; + sourceTree = "<group>"; + }; 4C9B0DEC2A65A74000CBDA21 /* Util */ = { isa = PBXGroup; children = ( @@ -1455,6 +1494,7 @@ 4CE6DEDA27F7A08100C66700 = { isa = PBXGroup; children = ( + 4C9054862A6AEB4500811EEC /* nostrdb */, 4C19AE4A2A5CEF7C00C90DB7 /* nostrscript */, 4C06670728FDE62900038D2A /* damus-c */, 4CE6DEE527F7A08100C66700 /* damus */, @@ -1511,6 +1551,7 @@ 4CE6DEF627F7A08200C66700 /* damusTests */ = { isa = PBXGroup; children = ( + 4C9054832A6AEA7B00811EEC /* NDB */, 4C9B0DEC2A65A74000CBDA21 /* Util */, 4C0C03962A61E2670098B3B8 /* Fixtures */, 4C7D097D2A0C58B900943473 /* WalletConnectTests.swift */, @@ -1845,6 +1886,7 @@ 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */, 4C30AC7829A577AB00E2BD5A /* EventCache.swift in Sources */, 4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */, + 4CDD1AE22A6B3074001CD4DF /* NdbTagsIterator.swift in Sources */, 4C216F34286F5ACD00040376 /* DMView.swift in Sources */, 4C3EA64428FF558100C48A62 /* sha256.c in Sources */, 4CCF9AAF2A1FDBDB00E03CFB /* VideoPlayer.swift in Sources */, @@ -1899,6 +1941,7 @@ F7F0BA272978E54D009531F3 /* ParticipantsView.swift in Sources */, 4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */, 4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */, + 4C5D5C992A6AF8F80024563C /* NdbNote.swift in Sources */, 4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */, 4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */, 4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */, @@ -2035,6 +2078,7 @@ 4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */, 4C8682872814DE470026224F /* ProfileView.swift in Sources */, 5C0707D12A1ECB38004E7B51 /* DamusLogoGradient.swift in Sources */, + 4CDD1AE02A6B305F001CD4DF /* NdbTagElem.swift in Sources */, 4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */, 4CF0ABD629817F5B00D66079 /* ReportView.swift in Sources */, 4C1A9A2729DDE31900516EAC /* TranslationSettingsView.swift in Sources */, @@ -2089,6 +2133,7 @@ 4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */, 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */, 4CE4F0F229D4FCFA005914DB /* DebouncedOnChange.swift in Sources */, + 4C5D5C9D2A6B2CB40024563C /* AsciiCharacter.swift in Sources */, 4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */, 4C9146FE2A2A87C200DDEA40 /* nostrscript.c in Sources */, 4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */, @@ -2114,6 +2159,7 @@ 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */, 3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */, 3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */, + 4C5D5C9A2A6AF8F80024563C /* NdbTagIterator.swift in Sources */, 4CE879502996B2BD00F758CC /* RelayStatusView.swift in Sources */, 4CC7AAF4297F18B400430951 /* ReplyDescription.swift in Sources */, 4C75EFA427FA577B0006080F /* PostView.swift in Sources */, @@ -2159,6 +2205,7 @@ 4C7D097E2A0C58B900943473 /* WalletConnectTests.swift in Sources */, 4CB883AA297612FF00DC99E7 /* ZapTests.swift in Sources */, 4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */, + 4C9054852A6AEAA000811EEC /* NdbTests.swift in Sources */, F944F56E29EA9CCC0067B3BF /* DamusParseContentTests.swift in Sources */, 3A5E47C72A4A76C800C0D090 /* TrieTests.swift in Sources */, 4CB883AE2976FA9300DC99E7 /* FormatTests.swift in Sources */, diff --git a/damusTests/NDB/NdbTests.swift b/damusTests/NDB/NdbTests.swift @@ -0,0 +1,48 @@ +// +// NDBIterTests.swift +// damusTests +// +// Created by William Casarin on 2023-07-21. +// + +import XCTest +@testable import damus + +final class NdbTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func test_ndb_note() throws { + let note = NdbNote.owned_from_json(json: test_contact_list_json) + XCTAssertNotNil(note) + guard let note else { return } + + let id = "20d0ff27d6fcb13de8366328c5b1a7af26bcac07f2e558fbebd5e9242e608c09" + XCTAssertEqual(hex_encode(note.id), id) + + XCTAssertEqual(note.tags().underestimatedCount, 786) + XCTAssertEqual(note.tags().underestimatedCount, 786) + + //let tags = note.tags() + for tag in note.tags() { + for elem in tag { + print("test_ndb_iterator \(elem.)") + } + } + + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/nostrdb/AsciiCharacter.swift b/nostrdb/AsciiCharacter.swift @@ -0,0 +1,35 @@ +// +// AsciiCharacter.swift +// damus +// +// Created by William Casarin on 2023-07-21. +// + +import Foundation + +struct AsciiCharacter: ExpressibleByStringLiteral { + private let value: UInt8 + + var cchar: CChar { + return CChar(bitPattern: value) + } + + init?(_ character: Character) { + guard let asciiValue = character.asciiValue, asciiValue < 128 else { + return nil + } + self.value = asciiValue + } + + // MARK: - ExpressibleByStringLiteral conformance + init(stringLiteral value: StringLiteralType) { + guard value.count == 1, let character = value.first, let ascii = AsciiCharacter(character) else { + fatalError("Invalid ASCII character initialization.") + } + self = ascii + } + + var character: Character { + return Character(UnicodeScalar(value)) + } +} diff --git a/nostrdb/NdbNote.swift b/nostrdb/NdbNote.swift @@ -0,0 +1,42 @@ +// +// NdbNote.swift +// damus +// +// Created by William Casarin on 2023-07-21. +// + +import Foundation + +struct NdbNote { + private var owned: Data? + let note: UnsafeMutablePointer<ndb_note> + + init(notePointer: UnsafeMutablePointer<ndb_note>, data: Data?) { + self.note = notePointer + self.owned = data + } + + var id: Data { + Data(buffer: UnsafeBufferPointer(start: ndb_note_id(note), count: 32)) + } + + func tags() -> TagsSequence { + return .init(note: note) + } + + static func owned_from_json(json: String, bufsize: Int = 2 << 18) -> NdbNote? { + var data = Data(capacity: bufsize) + guard var json_cstr = json.cString(using: .utf8) else { return nil } + + var note: UnsafeMutablePointer<ndb_note>? + + let len = data.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) -> Int in + return Int(ndb_note_from_json(&json_cstr, Int32(json_cstr.count), &note, bytes.baseAddress, Int32(bufsize))) + } + + guard let note else { return nil } + + // Create new Data with just the valid bytes + let validData = Data(bytes: &note.pointee, count: len) + return NdbNote(notePointer: note, data: validData) + }} diff --git a/nostrdb/NdbTagElem.swift b/nostrdb/NdbTagElem.swift @@ -0,0 +1,29 @@ +// +// NdbTagElem.swift +// damus +// +// Created by William Casarin on 2023-07-21. +// + +import Foundation + +struct NdbTagElem { + private let note: UnsafeMutablePointer<ndb_note> + private let tag: UnsafeMutablePointer<ndb_tag> + let index: Int32 + + init(note: UnsafeMutablePointer<ndb_note>, tag: UnsafeMutablePointer<ndb_tag>, index: Int32) { + self.note = note + self.tag = tag + self.index = index + } + + func matches_char(c: AsciiCharacter) -> Bool { + return ndb_tag_matches_char(note, tag, index, c.cchar) == 1 + } + + func string() -> String { + return String(cString: ndb_tag_str(note, tag, index), encoding: .utf8) ?? "" + } +} + diff --git a/nostrdb/NdbTagIterator.swift b/nostrdb/NdbTagIterator.swift @@ -0,0 +1,47 @@ +// +// NdbTagIterators.swift +// damus +// +// Created by William Casarin on 2023-07-21. +// + +import Foundation + +struct TagSequence: Sequence { + let note: UnsafeMutablePointer<ndb_note> + let tag: UnsafeMutablePointer<ndb_tag> + + func makeIterator() -> TagIterator { + return TagIterator(note: note, tag: tag) + } +} + +struct TagIterator: IteratorProtocol { + typealias Element = NdbTagElem + + mutating func next() -> NdbTagElem? { + guard index < tag.pointee.count else { return nil } + let el = NdbTagElem(note: note, tag: tag, index: index) + + index += 1 + + return el + } + + var index: Int32 + let note: UnsafeMutablePointer<ndb_note> + var tag: UnsafeMutablePointer<ndb_tag> + + init(note: UnsafeMutablePointer<ndb_note>, tag: UnsafeMutablePointer<ndb_tag>) { + self.note = note + self.tag = tag + self.index = 0 + } +} + + +func ndb_maybe_pointee<T>(_ p: UnsafeMutablePointer<T>!) -> T? { + guard p != nil else { return nil } + return p.pointee +} + diff --git a/nostrdb/NdbTagsIterator.swift b/nostrdb/NdbTagsIterator.swift @@ -0,0 +1,40 @@ +// +// NdbTagsIterator.swift +// damus +// +// Created by William Casarin on 2023-07-21. +// + +import Foundation + +struct TagsIterator: IteratorProtocol { + typealias Element = TagSequence + + var done: Bool + var iter: ndb_iterator + + mutating func next() -> TagSequence? { + guard !done else { return nil } + + let tag_seq = TagSequence(note: iter.note, tag: self.iter.tag) + + let ok = ndb_tags_iterate_next(&self.iter) + done = ok == 0 + + return tag_seq + } + + init(note: UnsafeMutablePointer<ndb_note>) { + self.iter = ndb_iterator() + let res = ndb_tags_iterate_start(note, &self.iter) + self.done = res == 0 + } +} + +struct TagsSequence: Sequence { + let note: UnsafeMutablePointer<ndb_note> + + func makeIterator() -> TagsIterator { + return .init(note: note) + } +}