Skip to content

Commit 62159ad

Browse files
committed
fix: correctly check equality for CaseInsensitiveMap
1 parent 9d6857b commit 62159ad

File tree

5 files changed

+65
-16
lines changed

5 files changed

+65
-16
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "4820850c-8916-47f5-a7e1-8880e6a00d22",
3+
"type": "bugfix",
4+
"description": "Fix errors in equality checks for `CaseInsensitiveMap` which affect `Headers` and `ValuesMap` implementations"
5+
}

runtime/runtime-core/api/runtime-core.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ public class aws/smithy/kotlin/runtime/collections/ValuesMapImpl : aws/smithy/ko
306306
public fun getAll (Ljava/lang/String;)Ljava/util/List;
307307
public fun getCaseInsensitiveName ()Z
308308
protected final fun getValues ()Ljava/util/Map;
309+
public fun hashCode ()I
309310
public fun isEmpty ()Z
310311
public fun names ()Ljava/util/Set;
311312
}

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/collections/CaseInsensitiveMap.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ package aws.smithy.kotlin.runtime.collections
66

77
import aws.smithy.kotlin.runtime.InternalApi
88

9-
private class CaseInsensitiveString(val s: String) {
10-
val hash: Int = s.lowercase().hashCode()
11-
override fun hashCode(): Int = hash
12-
override fun equals(other: Any?): Boolean = other is CaseInsensitiveString && other.s.equals(s, ignoreCase = true)
13-
override fun toString(): String = s
9+
private class CaseInsensitiveString(val original: String) {
10+
val normalized = original.lowercase()
11+
override fun hashCode() = normalized.hashCode()
12+
override fun equals(other: Any?) = other is CaseInsensitiveString && normalized == other.normalized
13+
override fun toString() = original
1414
}
1515

1616
private fun String.toInsensitive(): CaseInsensitiveString =
@@ -36,11 +36,11 @@ internal class CaseInsensitiveMap<Value> : MutableMap<String, Value> {
3636

3737
override val entries: MutableSet<MutableMap.MutableEntry<String, Value>>
3838
get() = impl.entries.map {
39-
Entry(it.key.s, it.value)
39+
Entry(it.key.normalized, it.value)
4040
}.toMutableSet()
4141

4242
override val keys: MutableSet<String>
43-
get() = impl.keys.map { it.s }.toMutableSet()
43+
get() = impl.keys.map { it.normalized }.toMutableSet()
4444

4545
override val values: MutableCollection<Value>
4646
get() = impl.values
@@ -57,6 +57,12 @@ internal class CaseInsensitiveMap<Value> : MutableMap<String, Value> {
5757

5858
override fun remove(key: String): Value? = impl.remove(key.toInsensitive())
5959

60+
override fun hashCode() = impl.hashCode()
61+
62+
override fun equals(other: Any?) = other is CaseInsensitiveMap<*> && impl == other.impl
63+
64+
override fun toString() = impl.toString()
65+
6066
private class Entry<Key, Value>(
6167
override val key: Key,
6268
override var value: Value,

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/collections/ValuesMap.kt

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,13 @@ public open class ValuesMapImpl<T>(
8989

9090
override fun isEmpty(): Boolean = values.isEmpty()
9191

92-
override fun equals(other: Any?): Boolean =
93-
other is ValuesMap<*> &&
94-
caseInsensitiveName == other.caseInsensitiveName &&
95-
names().let { names ->
96-
if (names.size != other.names().size) {
97-
return false
98-
}
99-
names.all { getAll(it) == other.getAll(it) }
100-
}
92+
override fun equals(other: Any?): Boolean = when (other) {
93+
is ValuesMapImpl<*> -> caseInsensitiveName == other.caseInsensitiveName && values == other.values
94+
is ValuesMap<*> -> caseInsensitiveName == other.caseInsensitiveName && entries() == other.entries()
95+
else -> false
96+
}
97+
98+
override fun hashCode(): Int = values.hashCode()
10199

102100
private fun Map<String, List<T>>.deepCopyValues(): Map<String, List<T>> = mapValues { (_, v) -> v.toList() }
103101
}

runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/CaseInsensitiveMapTest.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,43 @@ class CaseInsensitiveMapTest {
1818
assertEquals("json", map["content-type"])
1919
assertEquals("json", map["CONTENT-TYPE"])
2020
}
21+
22+
@Test
23+
fun testEquality() {
24+
val left = CaseInsensitiveMap<String>()
25+
left["A"] = "apple"
26+
left["B"] = "banana"
27+
left["C"] = "cherry"
28+
29+
val right = CaseInsensitiveMap<String>()
30+
right["c"] = "cherry"
31+
right["b"] = "banana"
32+
right["a"] = "apple"
33+
34+
assertEquals(left, right)
35+
}
36+
37+
@Test
38+
fun testEntriesEquality() {
39+
val left = CaseInsensitiveMap<String>()
40+
left["A"] = "apple"
41+
left["B"] = "banana"
42+
left["C"] = "cherry"
43+
44+
val right = CaseInsensitiveMap<String>()
45+
right["c"] = "cherry"
46+
right["b"] = "banana"
47+
right["a"] = "apple"
48+
49+
assertEquals(left.entries, right.entries)
50+
}
51+
52+
@Test
53+
fun testToString() {
54+
val map = CaseInsensitiveMap<String>()
55+
map["A"] = "apple"
56+
map["B"] = "banana"
57+
map["C"] = "cherry"
58+
assertEquals("{A=apple, B=banana, C=cherry}", map.toString())
59+
}
2160
}

0 commit comments

Comments
 (0)