diff --git a/src/main/java/it/smartphonecombo/uecapabilityparser/cli/HelpMessage.kt b/src/main/java/it/smartphonecombo/uecapabilityparser/cli/HelpMessage.kt index c0fb044f..f416481a 100644 --- a/src/main/java/it/smartphonecombo/uecapabilityparser/cli/HelpMessage.kt +++ b/src/main/java/it/smartphonecombo/uecapabilityparser/cli/HelpMessage.kt @@ -55,5 +55,5 @@ object HelpMessage { const val CUSTOM_CSS = "Inject custom css in Web UI" const val CUSTOM_JS = "Inject custom js in Web UI" const val LIBRARY_CACHE = - "Number of items to cache, each items occupies ~0.15MB of RAM. 0 = disabled, -1 = unlimited." + "Number of items to cache, each items occupies ~4-80KB of RAM. 0 = disabled, -1 = unlimited." } diff --git a/src/main/java/it/smartphonecombo/uecapabilityparser/util/Intern.kt b/src/main/java/it/smartphonecombo/uecapabilityparser/util/Intern.kt index 85076a8b..388c2718 100644 --- a/src/main/java/it/smartphonecombo/uecapabilityparser/util/Intern.kt +++ b/src/main/java/it/smartphonecombo/uecapabilityparser/util/Intern.kt @@ -1,38 +1,51 @@ package it.smartphonecombo.uecapabilityparser.util -import it.smartphonecombo.uecapabilityparser.model.EmptyMimo -import it.smartphonecombo.uecapabilityparser.model.Mimo -import it.smartphonecombo.uecapabilityparser.model.bandwidth.Bandwidth -import it.smartphonecombo.uecapabilityparser.model.bandwidth.EmptyBandwidth -import it.smartphonecombo.uecapabilityparser.model.modulation.EmptyModulation -import it.smartphonecombo.uecapabilityparser.model.modulation.Modulation +import it.smartphonecombo.uecapabilityparser.model.band.IBandDetails +import it.smartphonecombo.uecapabilityparser.model.combo.ICombo +import it.smartphonecombo.uecapabilityparser.model.component.IComponent open class InternMap(maxCapacity: Int) { + @Transient private val lock = Any() + private val internalMap: LinkedHashMap = - object : LinkedHashMap(minOf(16, maxCapacity), 0.75f) { + object : LinkedHashMap(computeInitialCapacity(maxCapacity)) { override fun removeEldestEntry(eldest: Map.Entry): Boolean { return size > maxCapacity } } private fun put(value: T): T { - internalMap[value] = value + synchronized(lock) { internalMap[value] = value } return value } fun intern(value: T): T = internalMap[value] ?: put(value) + + fun contains(value: T): Boolean = internalMap.contains(value) + + fun size() = internalMap.size + + companion object { + private fun computeInitialCapacity(maxCapacity: Int): Int { + // A value that ensures no re-hash is maxCapacity / 0.75 + 1 + // Compute that value / 2 + val initialCapacity = Math.floorDiv(maxCapacity * 2, 3) + 1 + + return maxOf(16, initialCapacity) + } + } } -object MimoInternMap : InternMap(100) +private object IBandDetailsInternMap : InternMap(1000) + +private object IComponentInternMap : InternMap(10000) -object ModulationInternMap : InternMap(100) +private object IComboInternMap : InternMap(100000) -object BandwidthInternMap : InternMap(100) +internal fun IBandDetails.intern() = IBandDetailsInternMap.intern(this) -internal fun Mimo.intern(): Mimo = if (this == EmptyMimo) this else MimoInternMap.intern(this) +internal fun IComponent.intern() = IComponentInternMap.intern(this) -internal fun Modulation.intern() = - if (this == EmptyModulation) this else ModulationInternMap.intern(this) +internal fun ICombo.intern() = IComboInternMap.intern(this) -internal fun Bandwidth.intern() = - if (this == EmptyBandwidth) this else BandwidthInternMap.intern(this) +internal fun ICombo.alreadyInterned() = IComboInternMap.contains(this) diff --git a/src/main/java/it/smartphonecombo/uecapabilityparser/util/Optimization.kt b/src/main/java/it/smartphonecombo/uecapabilityparser/util/Optimization.kt index 4d86545e..29c069e3 100644 --- a/src/main/java/it/smartphonecombo/uecapabilityparser/util/Optimization.kt +++ b/src/main/java/it/smartphonecombo/uecapabilityparser/util/Optimization.kt @@ -1,42 +1,48 @@ package it.smartphonecombo.uecapabilityparser.util -import it.smartphonecombo.uecapabilityparser.extension.trimToSize +import it.smartphonecombo.uecapabilityparser.extension.typedList import it.smartphonecombo.uecapabilityparser.model.Capabilities import it.smartphonecombo.uecapabilityparser.model.band.IBandDetails +import it.smartphonecombo.uecapabilityparser.model.combo.ComboEnDc +import it.smartphonecombo.uecapabilityparser.model.combo.ComboLte +import it.smartphonecombo.uecapabilityparser.model.combo.ComboNr +import it.smartphonecombo.uecapabilityparser.model.combo.ComboNrDc import it.smartphonecombo.uecapabilityparser.model.combo.ICombo -import it.smartphonecombo.uecapabilityparser.model.component.ComponentNr import it.smartphonecombo.uecapabilityparser.model.component.IComponent internal fun Capabilities.optimize() { - lteBands.forEach { it.optimize() } - nrBands.forEach { it.optimize() } - lteCombos.forEach { it.optimize() } - nrCombos.forEach { it.optimize() } - nrDcCombos.forEach { it.optimize() } - enDcCombos.forEach { it.optimize() } -} + // bands + lteBands = lteBands.map(IBandDetails::intern).typedList() + nrBands = nrBands.map(IBandDetails::intern).typedList() -internal fun IBandDetails.optimize() { - mimoDL = mimoDL.intern() - mimoUL = mimoUL.intern() - modDL = modDL.intern() - modUL = modUL.intern() + // combos + lteCombos = lteCombos.map(ICombo::optimizeAndIntern).typedList() + nrCombos = nrCombos.map(ICombo::optimizeAndIntern).typedList() + nrDcCombos = nrDcCombos.map(ICombo::optimizeAndIntern).typedList() + enDcCombos = enDcCombos.map(ICombo::optimizeAndIntern).typedList() } -internal fun ICombo.optimize() { - masterComponents.trimToSize() - masterComponents.forEach { it.optimize() } - secondaryComponents.trimToSize() - secondaryComponents.forEach { it.optimize() } +private fun ICombo.optimizeAndIntern(): ICombo { + val res = if (alreadyInterned()) this else optimized() + return res.intern() } -internal fun IComponent.optimize() { - mimoDL = mimoDL.intern() - mimoUL = mimoUL.intern() - modDL = modDL.intern() - modUL = modUL.intern() - if (this is ComponentNr) { - maxBandwidthDl = maxBandwidthDl.intern() - maxBandwidthUl = maxBandwidthUl.intern() +private fun ICombo.optimized(): ICombo { + val masterComponentsOpt = masterComponents.map(IComponent::intern) + val secondaryComponentsOpt = secondaryComponents.map(IComponent::intern) + + return when (this) { + is ComboLte -> copy(masterComponents = masterComponentsOpt.typedList()) + is ComboNr -> copy(masterComponents = masterComponentsOpt.typedList()) + is ComboNrDc -> + copy( + masterComponents = masterComponentsOpt.typedList(), + secondaryComponents = secondaryComponentsOpt.typedList(), + ) + is ComboEnDc -> + copy( + masterComponents = masterComponentsOpt.typedList(), + secondaryComponents = secondaryComponentsOpt.typedList(), + ) } }