diff --git a/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/RelayStats.kt b/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/RelayStats.kt index df0f5a5d1..b386b3f9c 100644 --- a/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/RelayStats.kt +++ b/ammolite/src/main/java/com/vitorpamplona/ammolite/relays/RelayStats.kt @@ -30,14 +30,14 @@ object RelayStats { fun addBytesReceived( url: String, - bytesUsedInMemory: Int, + bytesUsedInMemory: Long, ) { get(url).addBytesReceived(bytesUsedInMemory) } fun addBytesSent( url: String, - bytesUsedInMemory: Int, + bytesUsedInMemory: Long, ) { get(url).addBytesSent(bytesUsedInMemory) } @@ -102,11 +102,11 @@ class RelayStat( messages.put(debugMessage, debugMessage) } - fun addBytesReceived(bytesUsedInMemory: Int) { + fun addBytesReceived(bytesUsedInMemory: Long) { receivedBytes += bytesUsedInMemory } - fun addBytesSent(bytesUsedInMemory: Int) { + fun addBytesSent(bytesUsedInMemory: Long) { sentBytes += bytesUsedInMemory } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/encoders/ATag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/encoders/ATag.kt index dd4dcfc7d..5ae3d85f2 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/encoders/ATag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/encoders/ATag.kt @@ -22,6 +22,8 @@ package com.vitorpamplona.quartz.encoders import android.util.Log import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable data class ATag( @@ -30,6 +32,13 @@ data class ATag( val dTag: String, val relay: String?, ) { + fun countMemory(): Long = + 5 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit) + 8L + // kind + pubKeyHex.bytesUsedInMemory() + + dTag.bytesUsedInMemory() + + (relay?.bytesUsedInMemory() ?: 0) + fun toTag() = assembleATag(kind, pubKeyHex, dTag) fun toNAddr(): String = diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt index 5ccad15c8..c56eec3da 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/AppDefinitionEvent.kt @@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import java.io.ByteArrayInputStream @Stable @@ -58,6 +60,28 @@ class AppMetadata { @Transient var tags: ImmutableListOfLists? = null + fun countMemory(): Long = + 20 * pointerSizeInBytes + // 20 fields, 4 bytes for each reference + (name?.bytesUsedInMemory() ?: 0L) + + (username?.bytesUsedInMemory() ?: 0L) + + (displayName?.bytesUsedInMemory() ?: 0L) + + (picture?.bytesUsedInMemory() ?: 0L) + + (banner?.bytesUsedInMemory() ?: 0L) + + (image?.bytesUsedInMemory() ?: 0L) + + (website?.bytesUsedInMemory() ?: 0L) + + (about?.bytesUsedInMemory() ?: 0L) + + (subscription?.bytesUsedInMemory() ?: 0L) + + (cashuAccepted?.bytesUsedInMemory() ?: 0L) + + (encryptionSupported?.bytesUsedInMemory() ?: 0L) + + (personalized?.bytesUsedInMemory() ?: 0L) + // A Boolean has 8 bytes of header, plus 1 byte of payload, for a total of 9 bytes of information. The JVM then rounds it up to the next multiple of 8. so the one instance of java.lang.Boolean takes up 16 bytes of memory. + (amount?.bytesUsedInMemory() ?: 0L) + + (nip05?.bytesUsedInMemory() ?: 0L) + + (domain?.bytesUsedInMemory() ?: 0L) + + (lud06?.bytesUsedInMemory() ?: 0L) + + (lud16?.bytesUsedInMemory() ?: 0L) + + (twitter?.bytesUsedInMemory() ?: 0L) + + (tags?.lists?.sumOf { it.sumOf { it.bytesUsedInMemory() } } ?: 0L) + fun anyName(): String? = displayName ?: name ?: username fun anyNameStartsWith(prefix: String): Boolean = @@ -108,6 +132,8 @@ class AppDefinitionEvent( content: String, sig: HexKey, ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { + override fun countMemory(): Long = super.countMemory() + (cachedMetadata?.countMemory() ?: 8L) + @Transient private var cachedMetadata: AppMetadata? = null fun appMetaData() = diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelListEvent.kt index 489ed4b1c..3943c76b0 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/ChannelListEvent.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.collections.immutable.ImmutableSet @Immutable @@ -37,6 +39,10 @@ class ChannelListEvent( ) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient var publicAndPrivateEventCache: ImmutableSet? = null + override fun countMemory(): Long = + super.countMemory() + + 32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0L) // rough calculation + override fun dTag() = FIXED_D_TAG fun publicAndPrivateEvents( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/CommunityListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/CommunityListEvent.kt index 9ab0866af..f8e0d3dae 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/CommunityListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/CommunityListEvent.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.toImmutableSet @@ -39,6 +40,10 @@ class CommunityListEvent( ) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient var publicAndPrivateEventCache: ImmutableSet? = null + override fun countMemory(): Long = + super.countMemory() + + 32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.countMemory() } ?: 0L) // rough calculation + override fun dTag() = FIXED_D_TAG fun publicAndPrivateEvents( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt index ac0135674..28f297524 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/ContactListEvent.kt @@ -46,40 +46,35 @@ class ContactListEvent( content: String, sig: HexKey, ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { - // This function is only used by the user logged in - // But it is used all the time. - - @delegate:Transient - val verifiedFollowKeySet: Set by lazy { - tags.mapNotNullTo(HashSet()) { - try { - if (it.size > 1 && it[0] == "p") { + /** + * Returns a list of p-tags that are verified as hex keys. + */ + fun verifiedFollowKeySet(): Set = + tags.mapNotNullTo(mutableSetOf()) { + if (it.size > 1 && it[0] == "p") { + try { decodePublicKey(it[1]).toHexKey() - } else { + } catch (e: Exception) { + Log.w("ContactListEvent", "Can't parse p-tag $it in the contact list of $pubKey with id $id", e) null } - } catch (e: Exception) { - Log.w("ContactListEvent", "Can't parse tags as a follows: ${it[1]}", e) + } else { null } } - } - @delegate:Transient - val verifiedFollowTagSet: Set by lazy { - unverifiedFollowTagSet().map { it.lowercase() }.toSet() - } - - @delegate:Transient - val verifiedFollowGeohashSet: Set by lazy { - unverifiedFollowGeohashSet().map { it.lowercase() }.toSet() - } - - @delegate:Transient - val verifiedFollowCommunitySet: Set by lazy { unverifiedFollowAddressSet().toSet() } - - @delegate:Transient - val verifiedFollowKeySetAndMe: Set by lazy { verifiedFollowKeySet + pubKey } + /** + * Returns a list of a-tags that are verified as correct. + */ + fun verifiedFollowAddressSet(): Set = + tags + .mapNotNullTo(mutableSetOf()) { + if (it.size > 1 && it[0] == "a") { + ATag.parse(it[1], null)?.toTag() + } else { + null + } + } fun unverifiedFollowKeySet() = tags.filter { it.size > 1 && it[0] == "p" }.mapNotNull { it.getOrNull(1) } @@ -105,7 +100,7 @@ class ContactListEvent( } } - fun followsTags() = tags.filter { it.size > 1 && it[0] == "t" }.mapNotNull { it.getOrNull(1) } + fun followsTags() = hashtags() fun relays(): Map? = try { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/DraftEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/DraftEvent.kt index 7d19975df..db0274e36 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/DraftEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/DraftEvent.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class DraftEvent( @@ -37,6 +38,10 @@ class DraftEvent( ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient private var cachedInnerEvent: Map = mapOf() + override fun countMemory(): Long = + super.countMemory() + + 32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }) + override fun isContentEncoded() = true fun isDeleted() = content == "" diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt index 9c462846c..7437daf8a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt @@ -44,6 +44,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import java.math.BigDecimal import java.security.MessageDigest @@ -60,10 +61,11 @@ open class Event( override fun isContentEncoded() = false override fun countMemory(): Long = - 12L + + 7 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit) + 12L + // createdAt + kind id.bytesUsedInMemory() + pubKey.bytesUsedInMemory() + - tags.sumOf { it.sumOf { it.bytesUsedInMemory() } } + + tags.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } + content.bytesUsedInMemory() + sig.bytesUsedInMemory() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/GalleryListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/GalleryListEvent.kt index 808155184..97f5da251 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/GalleryListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/GalleryListEvent.kt @@ -60,7 +60,7 @@ class GalleryListEvent( createdAt: Long = TimeUtils.now(), onReady: (GalleryListEvent) -> Unit, ) { - var tags = arrayOf(tagName, url, eventid) + val tags = arrayOf(tagName, url, eventid) if (relay != null) { tags + relay } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/GeneralListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/GeneralListEvent.kt index 6256e7eca..f26b9f3bc 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/GeneralListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/GeneralListEvent.kt @@ -26,6 +26,8 @@ import com.fasterxml.jackson.module.kotlin.readValue import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.toImmutableSet import java.util.HashSet @@ -42,6 +44,10 @@ abstract class GeneralListEvent( ) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { @Transient private var privateTagsCache: Array>? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0) + override fun isContentEncoded() = true fun category() = dTag() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/GiftWrapEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/GiftWrapEvent.kt index 7e98dcd4b..b0e2e0ab8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/GiftWrapEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/GiftWrapEvent.kt @@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class GiftWrapEvent( @@ -38,6 +39,10 @@ class GiftWrapEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient private var cachedInnerEvent: Map = mapOf() + override fun countMemory(): Long = + super.countMemory() + + 32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }) // rough calculation + fun copyNoContent(): GiftWrapEvent { val copy = GiftWrapEvent( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapEvent.kt index 67a49a68c..e0dff9863 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapEvent.kt @@ -24,6 +24,7 @@ import android.util.Log import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.LnInvoiceUtil +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class LnZapEvent( @@ -38,6 +39,10 @@ class LnZapEvent( // This event is also kept in LocalCache (same object) @Transient val zapRequest: LnZapRequestEvent? + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (zapRequest?.countMemory() ?: 0) // rough calculation + override fun containedPost(): LnZapRequestEvent? = try { description()?.ifBlank { null }?.let { fromJson(it) } as? LnZapRequestEvent diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentRequestEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentRequestEvent.kt index 1727b0705..3efd97eaf 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentRequestEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentRequestEvent.kt @@ -29,6 +29,8 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class LnZapPaymentRequestEvent( @@ -42,6 +44,10 @@ class LnZapPaymentRequestEvent( // Once one of an app user decrypts the payment, all users else can see it. @Transient private var lnInvoice: String? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (lnInvoice?.bytesUsedInMemory() ?: 0) // rough calculation + fun walletServicePubKey() = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.get(1) fun talkingWith(oneSideHex: String): HexKey = if (pubKey == oneSideHex) walletServicePubKey() ?: pubKey else pubKey diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentResponseEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentResponseEvent.kt index 45e1320b1..60c83dc13 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentResponseEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPaymentResponseEvent.kt @@ -29,6 +29,8 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class LnZapPaymentResponseEvent( @@ -42,6 +44,8 @@ class LnZapPaymentResponseEvent( // Once one of an app user decrypts the payment, all users else can see it. @Transient private var response: Response? = null + override fun countMemory(): Long = super.countMemory() + pointerSizeInBytes + (response?.countMemory() ?: 0) + fun requestAuthor() = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.get(1) fun requestId() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1) @@ -91,7 +95,9 @@ class LnZapPaymentResponseEvent( // RESPONSE OBJECTS abstract class Response( @JsonProperty("result_type") val resultType: String, -) +) { + abstract fun countMemory(): Long +} // PayInvoice Call @@ -100,7 +106,11 @@ class PayInvoiceSuccessResponse( ) : Response("pay_invoice") { class PayInvoiceResultParams( val preimage: String, - ) + ) { + fun countMemory(): Long = pointerSizeInBytes + preimage.bytesUsedInMemory() + } + + override fun countMemory(): Long = pointerSizeInBytes + (result?.countMemory() ?: 0) } class PayInvoiceErrorResponse( @@ -109,7 +119,11 @@ class PayInvoiceErrorResponse( class PayInvoiceErrorParams( val code: ErrorType?, val message: String?, - ) + ) { + fun countMemory(): Long = pointerSizeInBytes + pointerSizeInBytes + (message?.bytesUsedInMemory() ?: 0) + } + + override fun countMemory(): Long = pointerSizeInBytes + (error?.countMemory() ?: 0) enum class ErrorType { @JsonProperty(value = "RATE_LIMITED") diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt index 33727f11b..ee3096b0e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt @@ -29,6 +29,7 @@ import com.vitorpamplona.quartz.encoders.hexToByteArray import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import java.nio.charset.Charset import java.security.SecureRandom import javax.crypto.BadPaddingException @@ -47,6 +48,8 @@ class LnZapRequestEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient private var privateZapEvent: LnZapPrivateEvent? = null + override fun countMemory(): Long = super.countMemory() + pointerSizeInBytes + (privateZapEvent?.countMemory() ?: 0) + fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] } fun zappedAuthor() = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/MuteListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/MuteListEvent.kt index a1e556813..a945a9294 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/MuteListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/MuteListEvent.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.collections.immutable.ImmutableSet @Immutable @@ -39,6 +41,11 @@ class MuteListEvent( @Transient var publicAndPrivateWordCache: ImmutableSet? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) + + pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) + override fun dTag() = FIXED_D_TAG fun publicAndPrivateUsersAndWords( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP90ContentDiscoveryResponseEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP90ContentDiscoveryResponseEvent.kt index 9e0ba0500..a2c1feb72 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP90ContentDiscoveryResponseEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP90ContentDiscoveryResponseEvent.kt @@ -26,6 +26,8 @@ import com.fasterxml.jackson.module.kotlin.readValue import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class NIP90ContentDiscoveryResponseEvent( @@ -38,6 +40,10 @@ class NIP90ContentDiscoveryResponseEvent( ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient var events: List? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (events?.sumOf { it.bytesUsedInMemory() } ?: 0) + fun innerTags(): List { if (content.isEmpty()) { return listOf() diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/OtsEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/OtsEvent.kt index 6dccecdaf..cefea7c98 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/OtsEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/OtsEvent.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.quartz.ots.VerifyResult import com.vitorpamplona.quartz.ots.op.OpSHA256 import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.coroutines.CancellationException import java.util.Base64 @@ -48,6 +49,10 @@ class OtsEvent( @Transient var verifiedTime: Long? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + Long.SIZE_BYTES // verifiedTime + override fun isContentEncoded() = true fun digestEvent() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt index c872f2d06..066822565 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/PeopleListEvent.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.collections.immutable.ImmutableSet @Immutable @@ -39,6 +41,11 @@ class PeopleListEvent( @Transient var publicAndPrivateWordCache: ImmutableSet? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) + + pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) + fun publicAndPrivateWords( signer: NostrSigner, onReady: (ImmutableSet) -> Unit, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateDmEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateDmEvent.kt index 6dd21b030..83b8d8770 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateDmEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateDmEvent.kt @@ -27,6 +27,8 @@ import com.vitorpamplona.quartz.encoders.HexValidator import com.vitorpamplona.quartz.encoders.Nip54InlineMetadata import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes import kotlinx.collections.immutable.persistentSetOf @Immutable @@ -41,6 +43,10 @@ class PrivateDmEvent( ChatroomKeyable { @Transient private var decryptedContent: Map = mapOf() + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (decryptedContent.values.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() }) + override fun isContentEncoded() = true /** diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateOutboxRelayListEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateOutboxRelayListEvent.kt index 07551542a..a26f54c05 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateOutboxRelayListEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/PrivateOutboxRelayListEvent.kt @@ -27,6 +27,8 @@ import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.bytesUsedInMemory +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class PrivateOutboxRelayListEvent( @@ -39,6 +41,10 @@ class PrivateOutboxRelayListEvent( ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient private var privateTagsCache: Array>? = null + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0) + override fun dTag() = FIXED_D_TAG fun relays(): List? = diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/SealedGossipEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/SealedGossipEvent.kt index 1ca4af9c6..d6b92a8e1 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/SealedGossipEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/SealedGossipEvent.kt @@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils +import com.vitorpamplona.quartz.utils.pointerSizeInBytes @Immutable class SealedGossipEvent( @@ -39,6 +40,10 @@ class SealedGossipEvent( ) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { @Transient private var cachedInnerEvent: Map = mapOf() + override fun countMemory(): Long = + super.countMemory() + + pointerSizeInBytes + cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) } + fun copyNoContent(): SealedGossipEvent { val copy = SealedGossipEvent( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/StringUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/StringUtils.kt index 305d487be..085856d6f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/StringUtils.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/StringUtils.kt @@ -22,7 +22,11 @@ package com.vitorpamplona.quartz.utils import kotlin.math.min -fun String.bytesUsedInMemory(): Int = (8 * ((((this.length) * 2) + 45) / 8)) +val pointerSizeInBytes = 4 + +fun String.bytesUsedInMemory(): Long = (8 * (((this.length * 2L) + 45) / 8)) + +fun Boolean.bytesUsedInMemory(): Long = 8 fun String.containsIgnoreCase(term: String): Boolean { if (term.isEmpty()) return true // Empty string is contained