feat: Enhance performance and usability in chat components and emoji handling
This commit is contained in:
@@ -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 ->
|
||||
|
||||
Reference in New Issue
Block a user