Merge "Use SieveCache instead of LruCache in .ui.text" into androidx-main
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index fa92639..47192fb 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -53,7 +53,9 @@
 
                 implementation(project(":compose:ui:ui-util"))
 
-                implementation("androidx.collection:collection:1.4.0")
+                // TODO: Pin androidx.collection when SieveCache is available
+                // implementation("androidx.collection:collection:1.x.0")
+                implementation(project(":collection:collection"))
             }
         }
 
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
index ba2a9c8..aaa9578 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
@@ -21,9 +21,7 @@
 import android.os.Build
 import android.util.TypedValue
 import androidx.annotation.RequiresApi
-import androidx.collection.LruCache
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.android.InternalPlatformTextApi
+import androidx.collection.SieveCache
 import androidx.compose.ui.text.font.AndroidFont
 import androidx.compose.ui.text.font.AndroidPreloadedFont
 import androidx.compose.ui.text.font.Font
@@ -110,18 +108,17 @@
 internal object AndroidTypefaceCache {
 
     // TODO multiple TypefaceCache's, would be good to unify
-    private val cache = LruCache<String, Typeface>(16)
+    private val cache = SieveCache<String, Typeface>(16, 16)
 
     /**
      * Returns NativeTypeface for [font] if it is in cache. Otherwise create new NativeTypeface and
      * put it into internal cache.
      */
-    @OptIn(InternalPlatformTextApi::class, ExperimentalTextApi::class)
     fun getOrCreate(context: Context, font: Font): Typeface {
         val key = getKey(context, font)
 
         key?.let {
-            cache.get(key)?.let {
+            cache[key]?.let {
                 return it
             }
         }
@@ -139,13 +136,13 @@
                 else -> throw IllegalArgumentException("Unknown font type: $font")
             } ?: throw IllegalArgumentException("Unable to load font $font")
 
-        key?.let { cache.put(key, typeface) }
+        key?.let { cache[key] = typeface }
 
         return typeface
     }
 
     /** Utility method to generate a key for caching purposes. */
-    fun getKey(context: Context, font: Font): String? {
+    private fun getKey(context: Context, font: Font): String? {
         return when (font) {
             is ResourceFont -> {
                 val value = TypedValue()
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
index 3ca3629..f3fa771 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.text
 
-import androidx.collection.LruCache
+import androidx.collection.SieveCache
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.text.font.FontFamily
@@ -80,8 +80,7 @@
  *   would miss the cache.
  */
 @Immutable
-class TextMeasurer
-constructor(
+class TextMeasurer(
     private val defaultFontFamilyResolver: FontFamily.Resolver,
     private val defaultDensity: Density,
     private val defaultLayoutDirection: LayoutDirection,
@@ -352,18 +351,18 @@
 }
 
 /**
- * Keeps an LRU layout cache of TextLayoutInput, TextLayoutResult pairs. Any non-layout affecting
- * change in TextLayoutInput (color, brush, shadow, TextDecoration) is ignored by this cache.
+ * Keeps a layout cache of TextLayoutInput, TextLayoutResult pairs. Any non-layout affecting change
+ * in TextLayoutInput (color, brush, shadow, TextDecoration) is ignored by this cache.
  *
- * @param capacity Maximum size of LRU cache. Size unit is the number of [CacheTextLayoutInput] and
+ * @param capacity Maximum size of the cache. Size unit is the number of [CacheTextLayoutInput] and
  *   [TextLayoutResult] pairs.
  * @throws IllegalArgumentException if capacity is not a positive integer.
  */
 internal class TextLayoutCache(capacity: Int = DefaultCacheSize) {
-    private val lruCache = LruCache<CacheTextLayoutInput, TextLayoutResult>(capacity)
+    private val cache = SieveCache<CacheTextLayoutInput, TextLayoutResult>(capacity, capacity)
 
     fun get(key: TextLayoutInput): TextLayoutResult? {
-        val resultFromCache = lruCache.get(CacheTextLayoutInput(key)) ?: return null
+        val resultFromCache = cache[CacheTextLayoutInput(key)] ?: return null
 
         if (resultFromCache.multiParagraph.intrinsics.hasStaleResolvedFonts) {
             // one of the resolved fonts has updated, and this MeasuredText is no longer valid for
@@ -375,11 +374,11 @@
     }
 
     fun put(key: TextLayoutInput, value: TextLayoutResult): TextLayoutResult? {
-        return lruCache.put(CacheTextLayoutInput(key), value)
+        return cache.put(CacheTextLayoutInput(key), value)
     }
 
     fun remove(key: TextLayoutInput): TextLayoutResult? {
-        return lruCache.remove(CacheTextLayoutInput(key))
+        return cache.remove(CacheTextLayoutInput(key))
     }
 }
 
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
index 05b31aa..004da07 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.text.font
 
-import androidx.collection.LruCache
+import androidx.collection.SieveCache
 import androidx.compose.runtime.State
 import androidx.compose.ui.text.platform.createSynchronizedObject
 import androidx.compose.ui.text.platform.synchronized
@@ -167,14 +167,14 @@
 internal class TypefaceRequestCache {
     internal val lock = createSynchronizedObject()
     // @GuardedBy("lock")
-    private val resultCache = LruCache<TypefaceRequest, TypefaceResult>(16)
+    private val resultCache = SieveCache<TypefaceRequest, TypefaceResult>(16, 16)
 
     fun runCached(
         typefaceRequest: TypefaceRequest,
         resolveTypeface: ((TypefaceResult) -> Unit) -> TypefaceResult
     ): State<Any> {
         synchronized(lock) {
-            resultCache.get(typefaceRequest)?.let {
+            resultCache[typefaceRequest]?.let {
                 if (it.cacheable) {
                     return it
                 } else {
@@ -216,7 +216,7 @@
         synchronized(lock) {
             // async result may have completed prior to this block entering, do not overwrite
             // final results
-            if (resultCache.get(typefaceRequest) == null && currentTypefaceResult.cacheable) {
+            if (resultCache[typefaceRequest] == null && currentTypefaceResult.cacheable) {
                 resultCache.put(typefaceRequest, currentTypefaceResult)
             }
         }
@@ -230,7 +230,7 @@
         for (i in typefaceRequests.indices) {
             val typeRequest = typefaceRequests[i]
 
-            val prior = synchronized(lock) { resultCache.get(typeRequest) }
+            val prior = synchronized(lock) { resultCache[typeRequest] }
             if (prior != null) continue
 
             val next =
@@ -244,15 +244,15 @@
             // has async fonts in permanent cache
             if (next is TypefaceResult.Async) continue
 
-            synchronized(lock) { resultCache.put(typeRequest, next) }
+            synchronized(lock) { resultCache[typeRequest] = next }
         }
     }
 
     // @VisibleForTesting
     internal fun get(typefaceRequest: TypefaceRequest) =
-        synchronized(lock) { resultCache.get(typefaceRequest) }
+        synchronized(lock) { resultCache[typefaceRequest] }
 
     // @VisibleForTesting
     internal val size: Int
-        get() = synchronized(lock) { resultCache.size() }
+        get() = synchronized(lock) { resultCache.size }
 }
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
index c7ebfd0..54c0aa1 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.ui.text.font
 
-import androidx.collection.LruCache
-import androidx.collection.SimpleArrayMap
+import androidx.collection.SieveCache
+import androidx.collection.mutableScatterMapOf
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -358,14 +358,14 @@
 
     internal data class Key(val font: Font, val loaderKey: Any?)
 
-    // 16 is based on the LruCache in TypefaceCompat Android, but no firm logic for this size.
+    // 16 is based on the cache in TypefaceCompat Android, but no firm logic for this size.
     // After loading, fonts are put into the resultCache to allow reading from a kotlin function
     // context, reducing async fonts overhead cache lookup overhead only while cached
     // @GuardedBy("cacheLock")
-    private val resultCache = LruCache<Key, AsyncTypefaceResult>(16)
+    private val resultCache = SieveCache<Key, AsyncTypefaceResult>(16, 16)
     // failures and preloads are permanent, so they are stored separately
     // @GuardedBy("cacheLock")
-    private val permanentCache = SimpleArrayMap<Key, AsyncTypefaceResult>()
+    private val permanentCache = mutableScatterMapOf<Key, AsyncTypefaceResult>()
 
     private val cacheLock = createSynchronizedObject()
 
@@ -379,13 +379,13 @@
         synchronized(cacheLock) {
             when {
                 result == null -> {
-                    permanentCache.put(key, PermanentFailure)
+                    permanentCache[key] = PermanentFailure
                 }
                 forever -> {
-                    permanentCache.put(key, AsyncTypefaceResult(result))
+                    permanentCache[key] = AsyncTypefaceResult(result)
                 }
                 else -> {
-                    resultCache.put(key, AsyncTypefaceResult(result))
+                    resultCache[key] = AsyncTypefaceResult(result)
                 }
             }
         }
@@ -393,7 +393,7 @@
 
     fun get(font: Font, platformFontLoader: PlatformFontLoader): AsyncTypefaceResult? {
         val key = Key(font, platformFontLoader.cacheKey)
-        return synchronized(cacheLock) { resultCache.get(key) ?: permanentCache[key] }
+        return synchronized(cacheLock) { resultCache[key] ?: permanentCache[key] }
     }
 
     suspend fun runCached(
@@ -404,7 +404,7 @@
     ): Any? {
         val key = Key(font, platformFontLoader.cacheKey)
         synchronized(cacheLock) {
-            val priorResult = resultCache.get(key) ?: permanentCache[key]
+            val priorResult = resultCache[key] ?: permanentCache[key]
             if (priorResult != null) {
                 return priorResult.result
             }
@@ -413,13 +413,13 @@
             synchronized(cacheLock) {
                 when {
                     it == null -> {
-                        permanentCache.put(key, PermanentFailure)
+                        permanentCache[key] = PermanentFailure
                     }
                     forever -> {
-                        permanentCache.put(key, AsyncTypefaceResult(it))
+                        permanentCache[key] = AsyncTypefaceResult(it)
                     }
                     else -> {
-                        resultCache.put(key, AsyncTypefaceResult(it))
+                        resultCache[key] = AsyncTypefaceResult(it)
                     }
                 }
             }
@@ -433,7 +433,7 @@
     ): Any? {
         synchronized(cacheLock) {
             val key = Key(font, platformFontLoader.cacheKey)
-            val priorResult = resultCache.get(key) ?: permanentCache[key]
+            val priorResult = resultCache[key] ?: permanentCache[key]
             if (priorResult != null) {
                 return priorResult.result
             }