feat: Enhance performance and usability in chat components and emoji handling

This commit is contained in:
2026-02-08 08:18:49 +05:00
parent 11a8ff7644
commit 8b8c883a63
7 changed files with 206 additions and 95 deletions

View File

@@ -18,6 +18,7 @@ import java.security.spec.PKCS8EncodedKeySpec
import java.io.ByteArrayOutputStream
import java.util.zip.Deflater
import java.util.zip.Inflater
import java.util.concurrent.ConcurrentHashMap
import com.google.crypto.tink.subtle.XChaCha20Poly1305
/**
@@ -39,15 +40,10 @@ object CryptoManager {
// Кэшируем производный ключ для каждого пароля чтобы не вычислять его каждый раз
private val pbkdf2KeyCache = mutableMapOf<String, SecretKeySpec>()
// 🚀 ОПТИМИЗАЦИЯ: LRU-кэш для расшифрованных сообщений
// Ключ = encryptedData, Значение = расшифрованный текст
// Ограничиваем размер чтобы не съесть память
private const val DECRYPTION_CACHE_SIZE = 500
private val decryptionCache = object : LinkedHashMap<String, String>(DECRYPTION_CACHE_SIZE, 0.75f, true) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, String>?): Boolean {
return size > DECRYPTION_CACHE_SIZE
}
}
// 🚀 ОПТИМИЗАЦИЯ: Lock-free кэш для расшифрованных сообщений
// ConcurrentHashMap вместо synchronized LinkedHashMap — убирает контention при параллельной расшифровке
private const val DECRYPTION_CACHE_SIZE = 2000
private val decryptionCache = ConcurrentHashMap<String, String>(DECRYPTION_CACHE_SIZE, 0.75f, 4)
init {
// Add BouncyCastle provider for secp256k1 support
@@ -73,9 +69,7 @@ object CryptoManager {
*/
fun clearCaches() {
pbkdf2KeyCache.clear()
synchronized(decryptionCache) {
decryptionCache.clear()
}
decryptionCache.clear()
keyPairCache.clear()
privateKeyHashCache.clear()
}
@@ -306,20 +300,22 @@ object CryptoManager {
* 🚀 ОПТИМИЗАЦИЯ: Кэширование PBKDF2 ключа и расшифрованных сообщений
*/
fun decryptWithPassword(encryptedData: String, password: String): String? {
// 🚀 ОПТИМИЗАЦИЯ: Проверяем кэш расшифрованных сообщений
// 🚀 ОПТИМИЗАЦИЯ: Lock-free проверка кэша (ConcurrentHashMap)
val cacheKey = "$password:$encryptedData"
synchronized(decryptionCache) {
decryptionCache[cacheKey]?.let { return it }
}
decryptionCache[cacheKey]?.let { return it }
return try {
val result = decryptWithPasswordInternal(encryptedData, password)
// 🚀 Сохраняем в кэш
// 🚀 Сохраняем в кэш (lock-free)
if (result != null) {
synchronized(decryptionCache) {
decryptionCache[cacheKey] = result
// Ограничиваем размер кэша
if (decryptionCache.size >= DECRYPTION_CACHE_SIZE) {
// Удаляем ~10% самых старых записей
val keysToRemove = decryptionCache.keys.take(DECRYPTION_CACHE_SIZE / 10)
keysToRemove.forEach { decryptionCache.remove(it) }
}
decryptionCache[cacheKey] = result
}
result
@@ -403,6 +399,11 @@ object CryptoManager {
* Check if data is in old format (base64-encoded hex with ":")
*/
private fun isOldFormat(data: String): Boolean {
// 🚀 Fast path: new format always contains ':' at plaintext level (iv:ct)
// Old format is a single base64 blob without ':' in the encoded string
if (data.contains(':')) return false
if (data.startsWith("CHNK:")) return false
return try {
val decoded = String(Base64.decode(data, Base64.NO_WRAP), Charsets.UTF_8)
decoded.contains(":") && decoded.split(":").all { part ->