Improves the accuracy of the Event memory counter.

This commit is contained in:
Vitor Pamplona
2024-08-06 15:35:14 -04:00
parent 71b45b96fb
commit e5328d7975
23 changed files with 172 additions and 39 deletions

View File

@@ -30,14 +30,14 @@ object RelayStats {
fun addBytesReceived( fun addBytesReceived(
url: String, url: String,
bytesUsedInMemory: Int, bytesUsedInMemory: Long,
) { ) {
get(url).addBytesReceived(bytesUsedInMemory) get(url).addBytesReceived(bytesUsedInMemory)
} }
fun addBytesSent( fun addBytesSent(
url: String, url: String,
bytesUsedInMemory: Int, bytesUsedInMemory: Long,
) { ) {
get(url).addBytesSent(bytesUsedInMemory) get(url).addBytesSent(bytesUsedInMemory)
} }
@@ -102,11 +102,11 @@ class RelayStat(
messages.put(debugMessage, debugMessage) messages.put(debugMessage, debugMessage)
} }
fun addBytesReceived(bytesUsedInMemory: Int) { fun addBytesReceived(bytesUsedInMemory: Long) {
receivedBytes += bytesUsedInMemory receivedBytes += bytesUsedInMemory
} }
fun addBytesSent(bytesUsedInMemory: Int) { fun addBytesSent(bytesUsedInMemory: Long) {
sentBytes += bytesUsedInMemory sentBytes += bytesUsedInMemory
} }

View File

@@ -22,6 +22,8 @@ package com.vitorpamplona.quartz.encoders
import android.util.Log import android.util.Log
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
data class ATag( data class ATag(
@@ -30,6 +32,13 @@ data class ATag(
val dTag: String, val dTag: String,
val relay: 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 toTag() = assembleATag(kind, pubKeyHex, dTag)
fun toNAddr(): String = fun toNAddr(): String =

View File

@@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
@Stable @Stable
@@ -58,6 +60,28 @@ class AppMetadata {
@Transient @Transient
var tags: ImmutableListOfLists<String>? = null var tags: ImmutableListOfLists<String>? = 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 anyName(): String? = displayName ?: name ?: username
fun anyNameStartsWith(prefix: String): Boolean = fun anyNameStartsWith(prefix: String): Boolean =
@@ -108,6 +132,8 @@ class AppDefinitionEvent(
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun countMemory(): Long = super.countMemory() + (cachedMetadata?.countMemory() ?: 8L)
@Transient private var cachedMetadata: AppMetadata? = null @Transient private var cachedMetadata: AppMetadata? = null
fun appMetaData() = fun appMetaData() =

View File

@@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
@Immutable @Immutable
@@ -37,6 +39,10 @@ class ChannelListEvent(
) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient var publicAndPrivateEventCache: ImmutableSet<HexKey>? = null @Transient var publicAndPrivateEventCache: ImmutableSet<HexKey>? = null
override fun countMemory(): Long =
super.countMemory() +
32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0L) // rough calculation
override fun dTag() = FIXED_D_TAG override fun dTag() = FIXED_D_TAG
fun publicAndPrivateEvents( fun publicAndPrivateEvents(

View File

@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
@@ -39,6 +40,10 @@ class CommunityListEvent(
) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : GeneralListEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient var publicAndPrivateEventCache: ImmutableSet<ATag>? = null @Transient var publicAndPrivateEventCache: ImmutableSet<ATag>? = null
override fun countMemory(): Long =
super.countMemory() +
32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.countMemory() } ?: 0L) // rough calculation
override fun dTag() = FIXED_D_TAG override fun dTag() = FIXED_D_TAG
fun publicAndPrivateEvents( fun publicAndPrivateEvents(

View File

@@ -46,40 +46,35 @@ class ContactListEvent(
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : 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. * Returns a list of p-tags that are verified as hex keys.
*/
@delegate:Transient fun verifiedFollowKeySet(): Set<HexKey> =
val verifiedFollowKeySet: Set<HexKey> by lazy { tags.mapNotNullTo(mutableSetOf()) {
tags.mapNotNullTo(HashSet()) { if (it.size > 1 && it[0] == "p") {
try { try {
if (it.size > 1 && it[0] == "p") {
decodePublicKey(it[1]).toHexKey() 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 null
} }
} catch (e: Exception) { } else {
Log.w("ContactListEvent", "Can't parse tags as a follows: ${it[1]}", e)
null null
} }
} }
}
@delegate:Transient /**
val verifiedFollowTagSet: Set<String> by lazy { * Returns a list of a-tags that are verified as correct.
unverifiedFollowTagSet().map { it.lowercase() }.toSet() */
} fun verifiedFollowAddressSet(): Set<HexKey> =
tags
@delegate:Transient .mapNotNullTo(mutableSetOf()) {
val verifiedFollowGeohashSet: Set<String> by lazy { if (it.size > 1 && it[0] == "a") {
unverifiedFollowGeohashSet().map { it.lowercase() }.toSet() ATag.parse(it[1], null)?.toTag()
} } else {
null
@delegate:Transient }
val verifiedFollowCommunitySet: Set<String> by lazy { unverifiedFollowAddressSet().toSet() } }
@delegate:Transient
val verifiedFollowKeySetAndMe: Set<HexKey> by lazy { verifiedFollowKeySet + pubKey }
fun unverifiedFollowKeySet() = tags.filter { it.size > 1 && it[0] == "p" }.mapNotNull { it.getOrNull(1) } 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<String, ReadWrite>? = fun relays(): Map<String, ReadWrite>? =
try { try {

View File

@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class DraftEvent( class DraftEvent(
@@ -37,6 +38,10 @@ class DraftEvent(
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf() @Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
override fun countMemory(): Long =
super.countMemory() +
32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) })
override fun isContentEncoded() = true override fun isContentEncoded() = true
fun isDeleted() = content == "" fun isDeleted() = content == ""

View File

@@ -44,6 +44,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import java.math.BigDecimal import java.math.BigDecimal
import java.security.MessageDigest import java.security.MessageDigest
@@ -60,10 +61,11 @@ open class Event(
override fun isContentEncoded() = false override fun isContentEncoded() = false
override fun countMemory(): Long = override fun countMemory(): Long =
12L + 7 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit)
12L + // createdAt + kind
id.bytesUsedInMemory() + id.bytesUsedInMemory() +
pubKey.bytesUsedInMemory() + pubKey.bytesUsedInMemory() +
tags.sumOf { it.sumOf { it.bytesUsedInMemory() } } + tags.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } +
content.bytesUsedInMemory() + content.bytesUsedInMemory() +
sig.bytesUsedInMemory() sig.bytesUsedInMemory()

View File

@@ -60,7 +60,7 @@ class GalleryListEvent(
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
onReady: (GalleryListEvent) -> Unit, onReady: (GalleryListEvent) -> Unit,
) { ) {
var tags = arrayOf(tagName, url, eventid) val tags = arrayOf(tagName, url, eventid)
if (relay != null) { if (relay != null) {
tags + relay tags + relay
} }

View File

@@ -26,6 +26,8 @@ import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner 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.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
import java.util.HashSet import java.util.HashSet
@@ -42,6 +44,10 @@ abstract class GeneralListEvent(
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { ) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
@Transient private var privateTagsCache: Array<Array<String>>? = null @Transient private var privateTagsCache: Array<Array<String>>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0)
override fun isContentEncoded() = true override fun isContentEncoded() = true
fun category() = dTag() fun category() = dTag()

View File

@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.signers.NostrSignerInternal
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class GiftWrapEvent( class GiftWrapEvent(
@@ -38,6 +39,10 @@ class GiftWrapEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf() @Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
override fun countMemory(): Long =
super.countMemory() +
32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }) // rough calculation
fun copyNoContent(): GiftWrapEvent { fun copyNoContent(): GiftWrapEvent {
val copy = val copy =
GiftWrapEvent( GiftWrapEvent(

View File

@@ -24,6 +24,7 @@ import android.util.Log
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class LnZapEvent( class LnZapEvent(
@@ -38,6 +39,10 @@ class LnZapEvent(
// This event is also kept in LocalCache (same object) // This event is also kept in LocalCache (same object)
@Transient val zapRequest: LnZapRequestEvent? @Transient val zapRequest: LnZapRequestEvent?
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (zapRequest?.countMemory() ?: 0) // rough calculation
override fun containedPost(): LnZapRequestEvent? = override fun containedPost(): LnZapRequestEvent? =
try { try {
description()?.ifBlank { null }?.let { fromJson(it) } as? LnZapRequestEvent description()?.ifBlank { null }?.let { fromJson(it) } as? LnZapRequestEvent

View File

@@ -29,6 +29,8 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class LnZapPaymentRequestEvent( class LnZapPaymentRequestEvent(
@@ -42,6 +44,10 @@ class LnZapPaymentRequestEvent(
// Once one of an app user decrypts the payment, all users else can see it. // Once one of an app user decrypts the payment, all users else can see it.
@Transient private var lnInvoice: String? = null @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 walletServicePubKey() = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.get(1)
fun talkingWith(oneSideHex: String): HexKey = if (pubKey == oneSideHex) walletServicePubKey() ?: pubKey else pubKey fun talkingWith(oneSideHex: String): HexKey = if (pubKey == oneSideHex) walletServicePubKey() ?: pubKey else pubKey

View File

@@ -29,6 +29,8 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class LnZapPaymentResponseEvent( class LnZapPaymentResponseEvent(
@@ -42,6 +44,8 @@ class LnZapPaymentResponseEvent(
// Once one of an app user decrypts the payment, all users else can see it. // Once one of an app user decrypts the payment, all users else can see it.
@Transient private var response: Response? = null @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 requestAuthor() = tags.firstOrNull { it.size > 1 && it[0] == "p" }?.get(1)
fun requestId() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1) fun requestId() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1)
@@ -91,7 +95,9 @@ class LnZapPaymentResponseEvent(
// RESPONSE OBJECTS // RESPONSE OBJECTS
abstract class Response( abstract class Response(
@JsonProperty("result_type") val resultType: String, @JsonProperty("result_type") val resultType: String,
) ) {
abstract fun countMemory(): Long
}
// PayInvoice Call // PayInvoice Call
@@ -100,7 +106,11 @@ class PayInvoiceSuccessResponse(
) : Response("pay_invoice") { ) : Response("pay_invoice") {
class PayInvoiceResultParams( class PayInvoiceResultParams(
val preimage: String, val preimage: String,
) ) {
fun countMemory(): Long = pointerSizeInBytes + preimage.bytesUsedInMemory()
}
override fun countMemory(): Long = pointerSizeInBytes + (result?.countMemory() ?: 0)
} }
class PayInvoiceErrorResponse( class PayInvoiceErrorResponse(
@@ -109,7 +119,11 @@ class PayInvoiceErrorResponse(
class PayInvoiceErrorParams( class PayInvoiceErrorParams(
val code: ErrorType?, val code: ErrorType?,
val message: String?, val message: String?,
) ) {
fun countMemory(): Long = pointerSizeInBytes + pointerSizeInBytes + (message?.bytesUsedInMemory() ?: 0)
}
override fun countMemory(): Long = pointerSizeInBytes + (error?.countMemory() ?: 0)
enum class ErrorType { enum class ErrorType {
@JsonProperty(value = "RATE_LIMITED") @JsonProperty(value = "RATE_LIMITED")

View File

@@ -29,6 +29,7 @@ import com.vitorpamplona.quartz.encoders.hexToByteArray
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.signers.NostrSignerInternal
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import java.nio.charset.Charset import java.nio.charset.Charset
import java.security.SecureRandom import java.security.SecureRandom
import javax.crypto.BadPaddingException import javax.crypto.BadPaddingException
@@ -47,6 +48,8 @@ class LnZapRequestEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var privateZapEvent: LnZapPrivateEvent? = null @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 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] } fun zappedAuthor() = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }

View File

@@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
@Immutable @Immutable
@@ -39,6 +41,11 @@ class MuteListEvent(
@Transient var publicAndPrivateWordCache: ImmutableSet<String>? = null @Transient var publicAndPrivateWordCache: ImmutableSet<String>? = 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 override fun dTag() = FIXED_D_TAG
fun publicAndPrivateUsersAndWords( fun publicAndPrivateUsersAndWords(

View File

@@ -26,6 +26,8 @@ import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class NIP90ContentDiscoveryResponseEvent( class NIP90ContentDiscoveryResponseEvent(
@@ -38,6 +40,10 @@ class NIP90ContentDiscoveryResponseEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient var events: List<HexKey>? = null @Transient var events: List<HexKey>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (events?.sumOf { it.bytesUsedInMemory() } ?: 0)
fun innerTags(): List<HexKey> { fun innerTags(): List<HexKey> {
if (content.isEmpty()) { if (content.isEmpty()) {
return listOf() return listOf()

View File

@@ -33,6 +33,7 @@ import com.vitorpamplona.quartz.ots.VerifyResult
import com.vitorpamplona.quartz.ots.op.OpSHA256 import com.vitorpamplona.quartz.ots.op.OpSHA256
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import java.util.Base64 import java.util.Base64
@@ -48,6 +49,10 @@ class OtsEvent(
@Transient @Transient
var verifiedTime: Long? = null var verifiedTime: Long? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + Long.SIZE_BYTES // verifiedTime
override fun isContentEncoded() = true override fun isContentEncoded() = true
fun digestEvent() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1) fun digestEvent() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1)

View File

@@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
@Immutable @Immutable
@@ -39,6 +41,11 @@ class PeopleListEvent(
@Transient var publicAndPrivateWordCache: ImmutableSet<String>? = null @Transient var publicAndPrivateWordCache: ImmutableSet<String>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (publicAndPrivateUserCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0) +
pointerSizeInBytes + (publicAndPrivateWordCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0)
fun publicAndPrivateWords( fun publicAndPrivateWords(
signer: NostrSigner, signer: NostrSigner,
onReady: (ImmutableSet<String>) -> Unit, onReady: (ImmutableSet<String>) -> Unit,

View File

@@ -27,6 +27,8 @@ import com.vitorpamplona.quartz.encoders.HexValidator
import com.vitorpamplona.quartz.encoders.Nip54InlineMetadata import com.vitorpamplona.quartz.encoders.Nip54InlineMetadata
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
@Immutable @Immutable
@@ -41,6 +43,10 @@ class PrivateDmEvent(
ChatroomKeyable { ChatroomKeyable {
@Transient private var decryptedContent: Map<HexKey, String> = mapOf() @Transient private var decryptedContent: Map<HexKey, String> = mapOf()
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (decryptedContent.values.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() })
override fun isContentEncoded() = true override fun isContentEncoded() = true
/** /**

View File

@@ -27,6 +27,8 @@ import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class PrivateOutboxRelayListEvent( class PrivateOutboxRelayListEvent(
@@ -39,6 +41,10 @@ class PrivateOutboxRelayListEvent(
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var privateTagsCache: Array<Array<String>>? = null @Transient private var privateTagsCache: Array<Array<String>>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0)
override fun dTag() = FIXED_D_TAG override fun dTag() = FIXED_D_TAG
fun relays(): List<String>? = fun relays(): List<String>? =

View File

@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
class SealedGossipEvent( class SealedGossipEvent(
@@ -39,6 +40,10 @@ class SealedGossipEvent(
) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf() @Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }
fun copyNoContent(): SealedGossipEvent { fun copyNoContent(): SealedGossipEvent {
val copy = val copy =
SealedGossipEvent( SealedGossipEvent(

View File

@@ -22,7 +22,11 @@ package com.vitorpamplona.quartz.utils
import kotlin.math.min 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 { fun String.containsIgnoreCase(term: String): Boolean {
if (term.isEmpty()) return true // Empty string is contained if (term.isEmpty()) return true // Empty string is contained