Skip to content

Commit cd2cb7c

Browse files
chore(client): use deep identity methods for primitive array types (#255)
1 parent 3e1766c commit cd2cb7c

File tree

3 files changed

+80
-32
lines changed

3 files changed

+80
-32
lines changed

openai-java-core/src/main/kotlin/com/openai/core/Utils.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,42 @@ internal fun <K : Comparable<K>, V> SortedMap<K, V>.toImmutable(): SortedMap<K,
2525
if (isEmpty()) Collections.emptySortedMap()
2626
else Collections.unmodifiableSortedMap(toSortedMap(comparator()))
2727

28+
/**
29+
* Returns whether [this] is equal to [other].
30+
*
31+
* This differs from [Object.equals] because it also deeply equates arrays based on their contents,
32+
* even when there are arrays directly nested within other arrays.
33+
*/
34+
@JvmSynthetic
35+
internal infix fun Any?.contentEquals(other: Any?): Boolean =
36+
arrayOf(this).contentDeepEquals(arrayOf(other))
37+
38+
/**
39+
* Returns a hash of the given sequence of [values].
40+
*
41+
* This differs from [java.util.Objects.hash] because it also deeply hashes arrays based on their
42+
* contents, even when there are arrays directly nested within other arrays.
43+
*/
44+
@JvmSynthetic internal fun contentHash(vararg values: Any?): Int = values.contentDeepHashCode()
45+
46+
/**
47+
* Returns a [String] representation of [this].
48+
*
49+
* This differs from [Object.toString] because it also deeply stringifies arrays based on their
50+
* contents, even when there are arrays directly nested within other arrays.
51+
*/
52+
@JvmSynthetic
53+
internal fun Any?.contentToString(): String {
54+
var string = arrayOf(this).contentDeepToString()
55+
if (string.startsWith('[')) {
56+
string = string.substring(1)
57+
}
58+
if (string.endsWith(']')) {
59+
string = string.substring(0, string.length - 1)
60+
}
61+
return string
62+
}
63+
2864
@JvmSynthetic
2965
internal fun isAzureEndpoint(baseUrl: String): Boolean {
3066
// Azure Endpoint should be in the format of `https://<region>.openai.azure.com`.

openai-java-core/src/main/kotlin/com/openai/core/Values.kt

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -470,41 +470,20 @@ internal constructor(
470470
val filename: String? = null,
471471
) {
472472

473-
private var hashCode: Int = 0
474-
475-
override fun hashCode(): Int {
476-
if (hashCode == 0) {
477-
hashCode =
478-
Objects.hash(
479-
name,
480-
contentType,
481-
filename,
482-
when (value) {
483-
is ByteArray -> value.contentHashCode()
484-
is String -> value
485-
is Boolean -> value
486-
is Long -> value
487-
is Double -> value
488-
else -> value?.hashCode()
489-
},
490-
)
491-
}
492-
return hashCode
493-
}
494-
495-
override fun equals(other: Any?): Boolean {
496-
if (this === other) return true
497-
if (other == null || this.javaClass != other.javaClass) return false
473+
private val hashCode: Int by lazy { contentHash(name, value, contentType, filename) }
498474

499-
other as MultipartFormValue<*>
475+
override fun hashCode(): Int = hashCode
500476

501-
if (name != other.name || contentType != other.contentType || filename != other.filename)
502-
return false
503-
504-
return when {
505-
value is ByteArray && other.value is ByteArray -> value contentEquals other.value
506-
else -> value?.equals(other.value) ?: (other.value == null)
477+
override fun equals(other: Any?): Boolean {
478+
if (this === other) {
479+
return true
507480
}
481+
482+
return other is MultipartFormValue<*> &&
483+
name == other.name &&
484+
value contentEquals other.value &&
485+
contentType == other.contentType &&
486+
filename == other.filename
508487
}
509488

510489
override fun toString(): String =
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.openai.core
2+
3+
import org.assertj.core.api.Assertions.assertThat
4+
import org.junit.jupiter.api.Test
5+
6+
internal class UtilsTest {
7+
@Test
8+
fun contentDeepEquals() {
9+
assertThat(42 contentEquals 42).isTrue()
10+
assertThat(42 contentEquals "Hello World!").isFalse()
11+
assertThat(byteArrayOf(1, 2, 3) contentEquals byteArrayOf(1, 2, 3)).isTrue()
12+
assertThat(byteArrayOf(1, 2, 3) contentEquals byteArrayOf(1, 2, 4)).isFalse()
13+
assertThat(
14+
arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) contentEquals
15+
arrayOf(byteArrayOf(1, 2), byteArrayOf(3))
16+
)
17+
.isTrue()
18+
assertThat(
19+
arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) contentEquals
20+
arrayOf(byteArrayOf(1), byteArrayOf(2, 3))
21+
)
22+
.isFalse()
23+
}
24+
25+
@Test
26+
fun contentToString() {
27+
assertThat((42).contentToString()).isEqualTo("42")
28+
assertThat("Hello World!".contentToString()).isEqualTo("Hello World!")
29+
assertThat(byteArrayOf(1, 2, 3).contentToString()).isEqualTo("[1, 2, 3]")
30+
assertThat(arrayOf(byteArrayOf(1, 2), byteArrayOf(3)).contentToString())
31+
.isEqualTo("[[1, 2], [3]]")
32+
}
33+
}

0 commit comments

Comments
 (0)