Explorar o código

Add `DeviceInfo.random(Random)` and implement `equals` and `hashCode` for `DeviceInfo`

Him188 %!s(int64=4) %!d(string=hai) anos
pai
achega
d5d0b35806

+ 6 - 0
binary-compatibility-validator/android/api/binary-compatibility-validator-android.api

@@ -5755,6 +5755,7 @@ public final class net/mamoe/mirai/utils/DeviceInfo {
 	public static final field Companion Lnet/mamoe/mirai/utils/DeviceInfo$Companion;
 	public synthetic fun <init> (I[B[B[B[B[B[B[B[B[B[B[BLnet/mamoe/mirai/utils/DeviceInfo$Version;[B[B[B[B[B[BLjava/lang/String;[BLkotlinx/serialization/internal/SerializationConstructorMarker;)V
 	public fun <init> ([B[B[B[B[B[B[B[B[B[B[BLnet/mamoe/mirai/utils/DeviceInfo$Version;[B[B[B[B[B[BLjava/lang/String;[B)V
+	public fun equals (Ljava/lang/Object;)Z
 	public static final fun from (Ljava/io/File;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public static final fun from (Ljava/io/File;Lkotlinx/serialization/json/Json;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public final fun getAndroidId ()[B
@@ -5780,7 +5781,9 @@ public final class net/mamoe/mirai/utils/DeviceInfo {
 	public final fun getVersion ()Lnet/mamoe/mirai/utils/DeviceInfo$Version;
 	public final fun getWifiBSSID ()[B
 	public final fun getWifiSSID ()[B
+	public fun hashCode ()I
 	public static final fun random ()Lnet/mamoe/mirai/utils/DeviceInfo;
+	public static final fun random (Lkotlin/random/Random;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public static final fun write$Self (Lnet/mamoe/mirai/utils/DeviceInfo;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
 }
 
@@ -5801,6 +5804,7 @@ public final class net/mamoe/mirai/utils/DeviceInfo$Companion {
 	public final fun from (Ljava/io/File;Lkotlinx/serialization/json/Json;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public static synthetic fun from$default (Lnet/mamoe/mirai/utils/DeviceInfo$Companion;Ljava/io/File;Lkotlinx/serialization/json/Json;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public final fun random ()Lnet/mamoe/mirai/utils/DeviceInfo;
+	public final fun random (Lkotlin/random/Random;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public final fun serializer ()Lkotlinx/serialization/KSerializer;
 }
 
@@ -5810,10 +5814,12 @@ public final class net/mamoe/mirai/utils/DeviceInfo$Version {
 	public synthetic fun <init> (I[B[B[BILkotlinx/serialization/internal/SerializationConstructorMarker;)V
 	public fun <init> ([B[B[BI)V
 	public synthetic fun <init> ([B[B[BIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public fun equals (Ljava/lang/Object;)Z
 	public final fun getCodename ()[B
 	public final fun getIncremental ()[B
 	public final fun getRelease ()[B
 	public final fun getSdk ()I
+	public fun hashCode ()I
 	public static final fun write$Self (Lnet/mamoe/mirai/utils/DeviceInfo$Version;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
 }
 

+ 6 - 0
binary-compatibility-validator/api/binary-compatibility-validator.api

@@ -5755,6 +5755,7 @@ public final class net/mamoe/mirai/utils/DeviceInfo {
 	public static final field Companion Lnet/mamoe/mirai/utils/DeviceInfo$Companion;
 	public synthetic fun <init> (I[B[B[B[B[B[B[B[B[B[B[BLnet/mamoe/mirai/utils/DeviceInfo$Version;[B[B[B[B[B[BLjava/lang/String;[BLkotlinx/serialization/internal/SerializationConstructorMarker;)V
 	public fun <init> ([B[B[B[B[B[B[B[B[B[B[BLnet/mamoe/mirai/utils/DeviceInfo$Version;[B[B[B[B[B[BLjava/lang/String;[B)V
+	public fun equals (Ljava/lang/Object;)Z
 	public static final fun from (Ljava/io/File;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public static final fun from (Ljava/io/File;Lkotlinx/serialization/json/Json;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public final fun getAndroidId ()[B
@@ -5780,7 +5781,9 @@ public final class net/mamoe/mirai/utils/DeviceInfo {
 	public final fun getVersion ()Lnet/mamoe/mirai/utils/DeviceInfo$Version;
 	public final fun getWifiBSSID ()[B
 	public final fun getWifiSSID ()[B
+	public fun hashCode ()I
 	public static final fun random ()Lnet/mamoe/mirai/utils/DeviceInfo;
+	public static final fun random (Lkotlin/random/Random;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public static final fun write$Self (Lnet/mamoe/mirai/utils/DeviceInfo;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
 }
 
@@ -5801,6 +5804,7 @@ public final class net/mamoe/mirai/utils/DeviceInfo$Companion {
 	public final fun from (Ljava/io/File;Lkotlinx/serialization/json/Json;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public static synthetic fun from$default (Lnet/mamoe/mirai/utils/DeviceInfo$Companion;Ljava/io/File;Lkotlinx/serialization/json/Json;ILjava/lang/Object;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public final fun random ()Lnet/mamoe/mirai/utils/DeviceInfo;
+	public final fun random (Lkotlin/random/Random;)Lnet/mamoe/mirai/utils/DeviceInfo;
 	public final fun serializer ()Lkotlinx/serialization/KSerializer;
 }
 
@@ -5810,10 +5814,12 @@ public final class net/mamoe/mirai/utils/DeviceInfo$Version {
 	public synthetic fun <init> (I[B[B[BILkotlinx/serialization/internal/SerializationConstructorMarker;)V
 	public fun <init> ([B[B[BI)V
 	public synthetic fun <init> ([B[B[BIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public fun equals (Ljava/lang/Object;)Z
 	public final fun getCodename ()[B
 	public final fun getIncremental ()[B
 	public final fun getRelease ()[B
 	public final fun getSdk ()I
+	public fun hashCode ()I
 	public static final fun write$Self (Lnet/mamoe/mirai/utils/DeviceInfo$Version;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
 }
 

+ 116 - 8
mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt

@@ -16,6 +16,7 @@ import kotlinx.serialization.json.Json
 import kotlinx.serialization.protobuf.ProtoBuf
 import kotlinx.serialization.protobuf.ProtoNumber
 import java.io.File
+import kotlin.random.Random
 
 @Serializable
 public class DeviceInfo(
@@ -57,7 +58,35 @@ public class DeviceInfo(
         public val release: ByteArray = "10".toByteArray(),
         public val codename: ByteArray = "REL".toByteArray(),
         public val sdk: Int = 29
-    )
+    ) {
+        /**
+         * @since 2.9
+         */
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Version
+
+            if (!incremental.contentEquals(other.incremental)) return false
+            if (!release.contentEquals(other.release)) return false
+            if (!codename.contentEquals(other.codename)) return false
+            if (sdk != other.sdk) return false
+
+            return true
+        }
+
+        /**
+         * @since 2.9
+         */
+        override fun hashCode(): Int {
+            var result = incremental.contentHashCode()
+            result = 31 * result + release.contentHashCode()
+            result = 31 * result + codename.contentHashCode()
+            result = 31 * result + sdk
+            return result
+        }
+    }
 
     public companion object {
         internal val logger = MiraiLogger.Factory.create(DeviceInfo::class, "DeviceInfo")
@@ -79,20 +108,36 @@ public class DeviceInfo(
             return json.decodeFromString(serializer(), this.readText())
         }
 
+        /**
+         * 生成随机 [DeviceInfo]
+         *
+         * @since 2.0
+         */
+        @JvmStatic
+        public fun random(): DeviceInfo = random(Random.Default)
 
+        /**
+         * 使用特定随机数生成器生成 [DeviceInfo]
+         *
+         * @since 2.9
+         */
         @JvmStatic
-        public fun random(): DeviceInfo {
+        public fun random(random: Random): DeviceInfo {
             return DeviceInfo(
-                display = "MIRAI.${getRandomString(6, '0'..'9')}.001".toByteArray(),
+                display = "MIRAI.${getRandomString(6, '0'..'9', random)}.001".toByteArray(),
                 product = "mirai".toByteArray(),
                 device = "mirai".toByteArray(),
                 board = "mirai".toByteArray(),
                 brand = "mamoe".toByteArray(),
                 model = "mirai".toByteArray(),
                 bootloader = "unknown".toByteArray(),
-                fingerprint = "mamoe/mirai/mirai:10/MIRAI.200122.001/${getRandomIntString(7)}:user/release-keys".toByteArray(),
-                bootId = generateUUID(getRandomByteArray(16).md5()).toByteArray(),
-                procVersion = "Linux version 3.0.31-${getRandomString(8)} ([email protected])".toByteArray(),
+                fingerprint = "mamoe/mirai/mirai:10/MIRAI.200122.001/${
+                    getRandomIntString(7, random)
+                }:user/release-keys".toByteArray(),
+                bootId = generateUUID(getRandomByteArray(16, random).md5()).toByteArray(),
+                procVersion = "Linux version 3.0.31-${
+                    getRandomString(8, random)
+                } ([email protected])".toByteArray(),
                 baseBand = byteArrayOf(),
                 version = Version(),
                 simInfo = "T-Mobile".toByteArray(),
@@ -100,12 +145,75 @@ public class DeviceInfo(
                 macAddress = "02:00:00:00:00:00".toByteArray(),
                 wifiBSSID = "02:00:00:00:00:00".toByteArray(),
                 wifiSSID = "<unknown ssid>".toByteArray(),
-                imsiMd5 = getRandomByteArray(16).md5(),
-                imei = getRandomIntString(15),
+                imsiMd5 = getRandomByteArray(16, random).md5(),
+                imei = getRandomIntString(15, random),
                 apn = "wifi".toByteArray()
             )
         }
     }
+
+    /**
+     * @since 2.9
+     */
+    @Suppress("DuplicatedCode")
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as DeviceInfo
+
+        if (!display.contentEquals(other.display)) return false
+        if (!product.contentEquals(other.product)) return false
+        if (!device.contentEquals(other.device)) return false
+        if (!board.contentEquals(other.board)) return false
+        if (!brand.contentEquals(other.brand)) return false
+        if (!model.contentEquals(other.model)) return false
+        if (!bootloader.contentEquals(other.bootloader)) return false
+        if (!fingerprint.contentEquals(other.fingerprint)) return false
+        if (!bootId.contentEquals(other.bootId)) return false
+        if (!procVersion.contentEquals(other.procVersion)) return false
+        if (!baseBand.contentEquals(other.baseBand)) return false
+        if (version != other.version) return false
+        if (!simInfo.contentEquals(other.simInfo)) return false
+        if (!osType.contentEquals(other.osType)) return false
+        if (!macAddress.contentEquals(other.macAddress)) return false
+        if (!wifiBSSID.contentEquals(other.wifiBSSID)) return false
+        if (!wifiSSID.contentEquals(other.wifiSSID)) return false
+        if (!imsiMd5.contentEquals(other.imsiMd5)) return false
+        if (imei != other.imei) return false
+        if (!apn.contentEquals(other.apn)) return false
+        if (!guid.contentEquals(other.guid)) return false
+
+        return true
+    }
+
+    /**
+     * @since 2.9
+     */
+    override fun hashCode(): Int {
+        var result = display.contentHashCode()
+        result = 31 * result + product.contentHashCode()
+        result = 31 * result + device.contentHashCode()
+        result = 31 * result + board.contentHashCode()
+        result = 31 * result + brand.contentHashCode()
+        result = 31 * result + model.contentHashCode()
+        result = 31 * result + bootloader.contentHashCode()
+        result = 31 * result + fingerprint.contentHashCode()
+        result = 31 * result + bootId.contentHashCode()
+        result = 31 * result + procVersion.contentHashCode()
+        result = 31 * result + baseBand.contentHashCode()
+        result = 31 * result + version.hashCode()
+        result = 31 * result + simInfo.contentHashCode()
+        result = 31 * result + osType.contentHashCode()
+        result = 31 * result + macAddress.contentHashCode()
+        result = 31 * result + wifiBSSID.contentHashCode()
+        result = 31 * result + wifiSSID.contentHashCode()
+        result = 31 * result + imsiMd5.contentHashCode()
+        result = 31 * result + imei.hashCode()
+        result = 31 * result + apn.contentHashCode()
+        result = 31 * result + guid.contentHashCode()
+        return result
+    }
 }
 
 @Serializable

+ 22 - 0
mirai-core-api/src/commonTest/kotlin/utils/DeviceInfoTest.kt

@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019-2021 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.utils
+
+import kotlin.random.Random
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class DeviceInfoTest {
+    @Test
+    fun `DeviceInfo_random with custom Random is stable`() {
+        val time = System.currentTimeMillis()
+        assertEquals(DeviceInfo.random(Random(time)), DeviceInfo.random(Random(time)))
+    }
+}

+ 10 - 9
mirai-core-utils/src/commonMain/kotlin/RandomUtils.kt

@@ -22,7 +22,8 @@ import kotlin.random.nextInt
 /**
  * 生成长度为 [length], 元素为随机 `0..255` 的 [ByteArray]
  */
-public fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() }
+public fun getRandomByteArray(length: Int, random: Random = Random): ByteArray =
+    ByteArray(length) { random.nextInt(0..255).toByte() }
 
 /**
  * 随机生成一个正整数
@@ -32,8 +33,8 @@ public fun getRandomUnsignedInt(): Int = Random.nextInt().absoluteValue
 /**
  * 随机生成长度为 [length] 的 [String].
  */
-public fun getRandomString(length: Int): String =
-    getRandomString(length, *defaultRanges)
+public fun getRandomString(length: Int, random: Random = Random): String =
+    getRandomString(length, *defaultRanges, random = random)
 
 private val defaultRanges: Array<CharRange> = arrayOf('a'..'z', 'A'..'Z', '0'..'9')
 private val intCharRanges: Array<CharRange> = arrayOf('0'..'9')
@@ -41,15 +42,15 @@ private val intCharRanges: Array<CharRange> = arrayOf('0'..'9')
 /**
  * 根据所给 [charRange] 随机生成长度为 [length] 的 [String].
  */
-public fun getRandomString(length: Int, charRange: CharRange): String =
-    CharArray(length) { charRange.random() }.concatToString()
+public fun getRandomString(length: Int, charRange: CharRange, random: Random = Random): String =
+    CharArray(length) { charRange.random(random) }.concatToString()
 
 /**
  * 根据所给 [charRanges] 随机生成长度为 [length] 的 [String].
  */
-public fun getRandomString(length: Int, vararg charRanges: CharRange): String =
-    CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() }.concatToString()
+public fun getRandomString(length: Int, vararg charRanges: CharRange, random: Random = Random): String =
+    CharArray(length) { charRanges[random.nextInt(0..charRanges.lastIndex)].random(random) }.concatToString()
 
 
-public fun getRandomIntString(length: Int): String =
-    getRandomString(length, *intCharRanges)
+public fun getRandomIntString(length: Int, random: Random = Random): String =
+    getRandomString(length, *intCharRanges, random = random)