1 /*
<lambda>null2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.work
17 
18 import android.annotation.SuppressLint
19 import androidx.annotation.RestrictTo
20 import androidx.annotation.VisibleForTesting
21 import androidx.room.TypeConverter
22 import java.io.ByteArrayInputStream
23 import java.io.ByteArrayOutputStream
24 import java.io.DataInputStream
25 import java.io.DataOutputStream
26 import java.io.IOException
27 import java.io.ObjectInputStream
28 import java.io.ObjectOutputStream
29 import java.io.ObjectStreamConstants
30 import java.util.Collections
31 import java.util.Objects
32 
33 /**
34  * A persistable set of key/value pairs which are used as inputs and outputs for
35  * [ListenableWorker]s. Keys are Strings, and values can be Strings, primitive types, or their array
36  * variants.
37  *
38  * This is a lightweight container, and should not be considered your data store. As such, there is
39  * an enforced [.MAX_DATA_BYTES] limit on the serialized (byte array) size of the payloads. This
40  * class will throw [IllegalStateException]s if you try to serialize or deserialize past this limit.
41  */
42 class Data {
43     private val values: Map<String, Any?>
44 
45     /** Copy constructor */
46     constructor(other: Data) {
47         values = HashMap(other.values)
48     }
49 
50     internal constructor(values: Map<String, *>) {
51         this.values = HashMap(values)
52     }
53 
54     private inline fun <reified T : Any> getOrDefault(key: String, defaultValue: T): T {
55         val value = values[key]
56         return if (value is T) value else defaultValue
57     }
58 
59     private inline fun <reified T : Any, TArray> getTypedArray(
60         key: String,
61         constructor: (size: Int, init: (index: Int) -> T) -> TArray
62     ): TArray? {
63         val value = values[key]
64         return if (value is Array<*> && value.isArrayOf<T>())
65             constructor(value.size) { i -> value[i] as T }
66         else null
67     }
68 
69     /**
70      * Gets the boolean value for the given key.
71      *
72      * @param key The key for the argument
73      * @param defaultValue The default value to return if the key is not found
74      * @return The value specified by the key if it exists; the default value otherwise
75      */
76     fun getBoolean(key: String, defaultValue: Boolean): Boolean = getOrDefault(key, defaultValue)
77 
78     /**
79      * Gets the boolean array value for the given key.
80      *
81      * @param key The key for the argument
82      * @return The value specified by the key if it exists; `null` otherwise
83      */
84     fun getBooleanArray(key: String): BooleanArray? = getTypedArray(key, ::BooleanArray)
85 
86     /**
87      * Gets the byte value for the given key.
88      *
89      * @param key The key for the argument
90      * @param defaultValue The default value to return if the key is not found
91      * @return The value specified by the key if it exists; the default value otherwise
92      */
93     fun getByte(key: String, defaultValue: Byte): Byte = getOrDefault(key, defaultValue)
94 
95     /**
96      * Gets the byte array value for the given key.
97      *
98      * @param key The key for the argument
99      * @return The value specified by the key if it exists; `null` otherwise
100      */
101     fun getByteArray(key: String): ByteArray? = getTypedArray(key, ::ByteArray)
102 
103     /**
104      * Gets the integer value for the given key.
105      *
106      * @param key The key for the argument
107      * @param defaultValue The default value to return if the key is not found
108      * @return The value specified by the key if it exists; the default value otherwise
109      */
110     fun getInt(key: String, defaultValue: Int): Int = getOrDefault(key, defaultValue)
111 
112     /**
113      * Gets the integer array value for the given key.
114      *
115      * @param key The key for the argument
116      * @return The value specified by the key if it exists; `null` otherwise
117      */
118     fun getIntArray(key: String): IntArray? = getTypedArray(key, ::IntArray)
119 
120     /**
121      * Gets the long value for the given key.
122      *
123      * @param key The key for the argument
124      * @param defaultValue The default value to return if the key is not found
125      * @return The value specified by the key if it exists; the default value otherwise
126      */
127     fun getLong(key: String, defaultValue: Long): Long = getOrDefault(key, defaultValue)
128 
129     /**
130      * Gets the long array value for the given key.
131      *
132      * @param key The key for the argument
133      * @return The value specified by the key if it exists; `null` otherwise
134      */
135     fun getLongArray(key: String): LongArray? = getTypedArray(key, ::LongArray)
136 
137     /**
138      * Gets the float value for the given key.
139      *
140      * @param key The key for the argument
141      * @param defaultValue The default value to return if the key is not found
142      * @return The value specified by the key if it exists; the default value otherwise
143      */
144     fun getFloat(key: String, defaultValue: Float): Float = getOrDefault(key, defaultValue)
145 
146     /**
147      * Gets the float array value for the given key.
148      *
149      * @param key The key for the argument
150      * @return The value specified by the key if it exists; `null` otherwise
151      */
152     fun getFloatArray(key: String): FloatArray? = getTypedArray(key, ::FloatArray)
153 
154     /**
155      * Gets the double value for the given key.
156      *
157      * @param key The key for the argument
158      * @param defaultValue The default value to return if the key is not found
159      * @return The value specified by the key if it exists; the default value otherwise
160      */
161     fun getDouble(key: String, defaultValue: Double): Double = getOrDefault(key, defaultValue)
162 
163     /**
164      * Gets the double array value for the given key.
165      *
166      * @param key The key for the argument
167      * @return The value specified by the key if it exists; `null` otherwise
168      */
169     fun getDoubleArray(key: String): DoubleArray? = getTypedArray(key, ::DoubleArray)
170 
171     /**
172      * Gets the String value for the given key.
173      *
174      * @param key The key for the argument
175      * @return The value specified by the key if it exists; `null` otherwise
176      */
177     fun getString(key: String): String? = values[key] as? String
178 
179     /**
180      * Gets the String array value for the given key.
181      *
182      * @param key The key for the argument
183      * @return The value specified by the key if it exists; `null` otherwise
184      */
185     fun getStringArray(key: String): Array<String>? = getTypedArray(key, ::Array)
186 
187     val keyValueMap: Map<String, Any?>
188         /**
189          * Gets all the values in this Data object.
190          *
191          * @return A [Map] of key-value pairs for this object; this Map is unmodifiable and should
192          *   be used for reads only.
193          */
194         get() = Collections.unmodifiableMap(values)
195 
196     /**
197      * Converts this Data to a byte array suitable for sending to other processes in your
198      * application. There are no versioning guarantees with this byte array, so you should not use
199      * this for IPCs between applications or persistence.
200      *
201      * @return The byte array representation of the input
202      * @throws IllegalStateException if the serialized payload is bigger than [.MAX_DATA_BYTES]
203      */
204     fun toByteArray(): ByteArray = toByteArrayInternalV1(this)
205 
206     /**
207      * Returns `true` if the instance of [Data] has a non-null value corresponding to the given
208      * [String] key with the expected type of `T`.
209      *
210      * @param key The [String] key
211      * @param klass The [Class] container for the expected type
212      * @return `true` If the instance of [Data] has a value for the given [String] key with the
213      *   expected type.
214      */
215     fun <T> hasKeyWithValueOfType(key: String, klass: Class<T>): Boolean {
216         val value = values[key]
217         return value != null && klass.isAssignableFrom(value.javaClass)
218     }
219 
220     /**
221      * Returns `true` if the instance of [Data] has a non-null value corresponding to the given
222      * [String] key with the expected type of [T].
223      *
224      * @param key The [String] key
225      * @return `true` If the instance of [Data] has a value for the given [String] key with the
226      *   expected type.
227      */
228     internal inline fun <reified T> hasKey(key: String): Boolean {
229         return hasKeyWithValueOfType(key, T::class.java)
230     }
231 
232     /** @return The number of elements in this Data object. */
233     @VisibleForTesting @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) fun size(): Int = values.size
234 
235     override fun equals(other: Any?): Boolean {
236         if (this === other) {
237             return true
238         }
239         if (other == null || javaClass != other.javaClass) {
240             return false
241         }
242         val otherData = other as Data
243         val keys = values.keys
244         if (keys != otherData.values.keys) {
245             return false
246         }
247         for (key in keys) {
248             val value = values[key]
249             val otherValue = otherData.values[key]
250             val equal =
251                 if (value == null || otherValue == null) {
252                     value === otherValue
253                 } else if (
254                     value is Array<*> &&
255                         value.isArrayOf<Any>() &&
256                         otherValue is Array<*> &&
257                         otherValue.isArrayOf<Any>()
258                 ) {
259                     value.contentDeepEquals(otherValue)
260                 } else {
261                     value == otherValue
262                 }
263             if (!equal) return false
264         }
265         return true
266     }
267 
268     override fun hashCode(): Int {
269         var h = 0
270         for (entry in values.entries) {
271             val value = entry.value
272             h +=
273                 if (value is Array<*>) {
274                     Objects.hashCode(entry.key) xor value.contentDeepHashCode()
275                 } else {
276                     entry.hashCode()
277                 }
278         }
279         return 31 * h
280     }
281 
282     override fun toString(): String = buildString {
283         append("Data {")
284         val content =
285             values.entries.joinToString { (key, value) ->
286                 "$key : ${if (value is Array<*>) value.contentToString() else value}"
287             }
288         append(content)
289         append("}")
290     }
291 
292     /** A builder for [Data] objects. */
293     class Builder {
294         private val values: MutableMap<String, Any?> = mutableMapOf()
295 
296         private fun putDirect(key: String, value: Any?): Builder {
297             values[key] = value
298             return this
299         }
300 
301         /**
302          * Puts a boolean into the arguments.
303          *
304          * @param key The key for this argument
305          * @param value The value for this argument
306          * @return The [Builder]
307          */
308         fun putBoolean(key: String, value: Boolean): Builder = putDirect(key, value)
309 
310         /**
311          * Puts a boolean array into the arguments.
312          *
313          * @param key The key for this argument
314          * @param value The value for this argument
315          * @return The [Builder]
316          */
317         fun putBooleanArray(key: String, value: BooleanArray): Builder {
318             values[key] = convertPrimitiveArray(value)
319             return this
320         }
321 
322         /**
323          * Puts an byte into the arguments.
324          *
325          * @param key The key for this argument
326          * @param value The value for this argument
327          * @return The [Builder]
328          */
329         fun putByte(key: String, value: Byte): Builder = putDirect(key, value)
330 
331         /**
332          * Puts an integer array into the arguments.
333          *
334          * @param key The key for this argument
335          * @param value The value for this argument
336          * @return The [Builder]
337          */
338         fun putByteArray(key: String, value: ByteArray): Builder {
339             values[key] = convertPrimitiveArray(value)
340             return this
341         }
342 
343         /**
344          * Puts an integer into the arguments.
345          *
346          * @param key The key for this argument
347          * @param value The value for this argument
348          * @return The [Builder]
349          */
350         fun putInt(key: String, value: Int): Builder = putDirect(key, value)
351 
352         /**
353          * Puts an integer array into the arguments.
354          *
355          * @param key The key for this argument
356          * @param value The value for this argument
357          * @return The [Builder]
358          */
359         fun putIntArray(key: String, value: IntArray): Builder {
360             values[key] = convertPrimitiveArray(value)
361             return this
362         }
363 
364         /**
365          * Puts a long into the arguments.
366          *
367          * @param key The key for this argument
368          * @param value The value for this argument
369          * @return The [Builder]
370          */
371         fun putLong(key: String, value: Long): Builder = putDirect(key, value)
372 
373         /**
374          * Puts a long array into the arguments.
375          *
376          * @param key The key for this argument
377          * @param value The value for this argument
378          * @return The [Builder]
379          */
380         fun putLongArray(key: String, value: LongArray): Builder {
381             values[key] = convertPrimitiveArray(value)
382             return this
383         }
384 
385         /**
386          * Puts a float into the arguments.
387          *
388          * @param key The key for this argument
389          * @param value The value for this argument
390          * @return The [Builder]
391          */
392         fun putFloat(key: String, value: Float): Builder = putDirect(key, value)
393 
394         /**
395          * Puts a float array into the arguments.
396          *
397          * @param key The key for this argument
398          * @param value The value for this argument
399          * @return The [Builder]
400          */
401         fun putFloatArray(key: String, value: FloatArray): Builder {
402             values[key] = convertPrimitiveArray(value)
403             return this
404         }
405 
406         /**
407          * Puts a double into the arguments.
408          *
409          * @param key The key for this argument
410          * @param value The value for this argument
411          * @return The [Builder]
412          */
413         fun putDouble(key: String, value: Double): Builder = putDirect(key, value)
414 
415         /**
416          * Puts a double array into the arguments.
417          *
418          * @param key The key for this argument
419          * @param value The value for this argument
420          * @return The [Builder]
421          */
422         fun putDoubleArray(key: String, value: DoubleArray): Builder {
423             values[key] = convertPrimitiveArray(value)
424             return this
425         }
426 
427         /**
428          * Puts a String into the arguments.
429          *
430          * @param key The key for this argument
431          * @param value The value for this argument
432          * @return The [Builder]
433          */
434         fun putString(key: String, value: String?): Builder = putDirect(key, value)
435 
436         /**
437          * Puts a String array into the arguments.
438          *
439          * @param key The key for this argument
440          * @param value The value for this argument
441          * @return The [Builder]
442          */
443         fun putStringArray(key: String, value: Array<String?>): Builder = putDirect(key, value)
444 
445         /**
446          * Puts all input key-value pairs from a [Data] into the Builder.
447          *
448          * Valid value types are: Boolean, Integer, Long, Float, Double, String, and their array
449          * versions. Invalid types will throw an [IllegalArgumentException].
450          *
451          * @param data [Data] containing key-value pairs to add
452          * @return The [Builder]
453          */
454         fun putAll(data: Data): Builder {
455             putAll(data.values)
456             return this
457         }
458 
459         /**
460          * Puts all input key-value pairs from a [Map] into the Builder.
461          *
462          * Valid value types are: Boolean, Integer, Long, Float, Double, String, and their array
463          * versions. Invalid types will throw an [IllegalArgumentException].
464          *
465          * @param values A [Map] of key-value pairs to add
466          * @return The [Builder]
467          */
468         fun putAll(values: Map<String, Any?>): Builder {
469             values.forEach { (key, value) -> put(key, value) }
470             return this
471         }
472 
473         /**
474          * Puts an input key-value pair into the Builder. Valid types are: Boolean, Integer, Long,
475          * Float, Double, String, and array versions of each of those types. Invalid types throw an
476          * [IllegalArgumentException].
477          *
478          * @param key A [String] key to add
479          * @param value A nullable [Object] value to add of the valid types
480          * @return The [Builder]
481          */
482         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
483         fun put(key: String, value: Any?): Builder {
484             values[key] =
485                 if (value == null) {
486                     null
487                 } else {
488                     when (val valueType = value::class) {
489                         Boolean::class,
490                         Byte::class,
491                         Int::class,
492                         Long::class,
493                         Float::class,
494                         Double::class,
495                         String::class,
496                         Array<Boolean>::class,
497                         Array<Byte>::class,
498                         Array<Int>::class,
499                         Array<Long>::class,
500                         Array<Float>::class,
501                         Array<Double>::class,
502                         Array<String>::class -> value
503                         BooleanArray::class -> convertPrimitiveArray(value as BooleanArray)
504                         ByteArray::class -> convertPrimitiveArray(value as ByteArray)
505                         IntArray::class -> convertPrimitiveArray(value as IntArray)
506                         LongArray::class -> convertPrimitiveArray(value as LongArray)
507                         FloatArray::class -> convertPrimitiveArray(value as FloatArray)
508                         DoubleArray::class -> convertPrimitiveArray(value as DoubleArray)
509                         else ->
510                             throw IllegalArgumentException("Key $key has invalid type $valueType")
511                     }
512                 }
513             return this
514         }
515 
516         /**
517          * Builds a [Data] object.
518          *
519          * @return The [Data] object containing all key-value pairs specified by this [Builder].
520          */
521         fun build(): Data {
522             val data = Data(values)
523             // Make sure we catch Data objects that are too large at build() instead of later.  This
524             // method will throw an exception if data is too big.
525             toByteArrayInternalV1(data)
526             return data
527         }
528     }
529 
530     companion object {
531         /** An empty Data object with no elements. */
532         @JvmField val EMPTY = Builder().build()
533 
534         /**
535          * The maximum number of bytes for Data when it is serialized (converted to a byte array).
536          * Please see the class-level Javadoc for more information.
537          */
538         @SuppressLint("MinMaxConstant") const val MAX_DATA_BYTES = 10 * 1024 // 10KB
539 
540         /** The list of supported types. */
541         private const val TYPE_NULL: Byte = 0
542         private const val TYPE_BOOLEAN: Byte = 1
543         private const val TYPE_BYTE: Byte = 2
544         private const val TYPE_INTEGER: Byte = 3
545         private const val TYPE_LONG: Byte = 4
546         private const val TYPE_FLOAT: Byte = 5
547         private const val TYPE_DOUBLE: Byte = 6
548         private const val TYPE_STRING: Byte = 7
549         private const val TYPE_BOOLEAN_ARRAY: Byte = 8
550         private const val TYPE_BYTE_ARRAY: Byte = 9
551         private const val TYPE_INTEGER_ARRAY: Byte = 10
552         private const val TYPE_LONG_ARRAY: Byte = 11
553         private const val TYPE_FLOAT_ARRAY: Byte = 12
554         private const val TYPE_DOUBLE_ARRAY: Byte = 13
555         private const val TYPE_STRING_ARRAY: Byte = 14
556 
557         /** Denotes `null` in a String array. */
558         private const val NULL_STRING_V1 = "androidx.work.Data-95ed6082-b8e9-46e8-a73f-ff56f00f5d9d"
559 
560         /** Magic number used in stream header. */
561         private const val STREAM_MAGIC: Short = 0xabef.toShort()
562 
563         /** Version number used in stream header. */
564         private const val STREAM_VERSION: Short = 1
565 
566         /**
567          * Converts [Data] to a byte array for persistent storage.
568          *
569          * @param data The [Data] object to convert
570          * @return The byte array representation of the input
571          * @throws IllegalStateException if the serialized payload is bigger than [.MAX_DATA_BYTES]
572          */
573         @JvmStatic
574         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
575         @Deprecated(
576             message = "This is kept for testing migration",
577             replaceWith = ReplaceWith("toByteArrayInternalV1")
578         )
579         fun toByteArrayInternalV0(data: Data): ByteArray {
580             return try {
581                 val stream =
582                     ByteArrayOutputStream().use { outputStream ->
583                         ObjectOutputStream(outputStream).use { objectOutputStream ->
584                             objectOutputStream.writeInt(data.size())
585                             for ((key, value) in data.values) {
586                                 objectOutputStream.writeUTF(key)
587                                 objectOutputStream.writeObject(value)
588                             }
589                         }
590                         outputStream
591                     }
592                 if (stream.size() > MAX_DATA_BYTES)
593                     throw IllegalStateException(
594                         "Data cannot occupy more than $MAX_DATA_BYTES bytes when serialized"
595                     )
596 
597                 stream.toByteArray()
598             } catch (e: IOException) {
599                 loge(TAG, e) { "Error in Data#toByteArray: " }
600                 ByteArray(0)
601             }
602         }
603 
604         @JvmStatic
605         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
606         @TypeConverter
607         fun toByteArrayInternalV1(data: Data): ByteArray {
608             fun DataOutputStream.writeHeader() {
609                 // We use our own magic and it's different from the
610                 // `ObjectStreamConstants.STREAM_MAGIC` used in V0.
611                 writeShort(STREAM_MAGIC.toInt())
612                 writeShort(STREAM_VERSION.toInt())
613             }
614 
615             fun DataOutputStream.writeArray(array: Array<*>) {
616                 val type =
617                     when (array::class) {
618                         Array<Boolean>::class -> TYPE_BOOLEAN_ARRAY
619                         Array<Byte>::class -> TYPE_BYTE_ARRAY
620                         Array<Int>::class -> TYPE_INTEGER_ARRAY
621                         Array<Long>::class -> TYPE_LONG_ARRAY
622                         Array<Float>::class -> TYPE_FLOAT_ARRAY
623                         Array<Double>::class -> TYPE_DOUBLE_ARRAY
624                         Array<String>::class -> TYPE_STRING_ARRAY
625                         else -> {
626                             throw IllegalArgumentException(
627                                 "Unsupported value type ${array::class.qualifiedName}"
628                             )
629                         }
630                     }
631                 writeByte(type.toInt())
632                 writeInt(array.size)
633                 for (element in array) {
634                     when (type) {
635                         TYPE_BOOLEAN_ARRAY -> writeBoolean(element as? Boolean ?: false)
636                         TYPE_BYTE_ARRAY -> writeByte((element as? Byte)?.toInt() ?: 0)
637                         TYPE_INTEGER_ARRAY -> writeInt(element as? Int ?: 0)
638                         TYPE_LONG_ARRAY -> writeLong(element as? Long ?: 0L)
639                         TYPE_FLOAT_ARRAY -> writeFloat(element as? Float ?: 0f)
640                         TYPE_DOUBLE_ARRAY -> writeDouble(element as? Double ?: 0.0)
641                         TYPE_STRING_ARRAY -> writeUTF(element as? String ?: NULL_STRING_V1)
642                     }
643                 }
644             }
645 
646             fun DataOutputStream.writeEntry(key: String, value: Any?) {
647                 // type + value
648                 when (value) {
649                     null -> writeByte(TYPE_NULL.toInt())
650                     is Boolean -> {
651                         writeByte(TYPE_BOOLEAN.toInt())
652                         writeBoolean(value)
653                     }
654                     is Byte -> {
655                         writeByte(TYPE_BYTE.toInt())
656                         writeByte(value.toInt())
657                     }
658                     is Int -> {
659                         writeByte(TYPE_INTEGER.toInt())
660                         writeInt(value)
661                     }
662                     is Long -> {
663                         writeByte(TYPE_LONG.toInt())
664                         writeLong(value)
665                     }
666                     is Float -> {
667                         writeByte(TYPE_FLOAT.toInt())
668                         writeFloat(value)
669                     }
670                     is Double -> {
671                         writeByte(TYPE_DOUBLE.toInt())
672                         writeDouble(value)
673                     }
674                     is String -> {
675                         writeByte(TYPE_STRING.toInt())
676                         writeUTF(value)
677                     }
678                     is Array<*> -> {
679                         writeArray(value)
680                     }
681                     else -> {
682                         // Exhaustive check
683                         throw IllegalArgumentException(
684                             "Unsupported value type ${value::class.simpleName}"
685                         )
686                     }
687                 }
688                 // key
689                 writeUTF(key)
690             }
691 
692             return try {
693                 ByteArrayOutputStream().let { outputStream ->
694                     DataOutputStream(outputStream).use {
695                         it.apply {
696                             writeHeader()
697                             writeInt(data.size())
698                             for ((key, value) in data.values) {
699                                 writeEntry(key, value)
700                             }
701                             flush()
702                         }
703                         check(it.size() <= MAX_DATA_BYTES) {
704                             "Data cannot occupy more than $MAX_DATA_BYTES bytes when serialized"
705                         }
706                         outputStream.toByteArray()
707                     }
708                 }
709             } catch (e: IOException) {
710                 loge(TAG, e) { "Error in Data#toByteArray: " }
711                 ByteArray(0)
712             }
713         }
714 
715         /**
716          * Converts a byte array to [Data].
717          *
718          * @param bytes The byte array representation to convert
719          * @return An [Data] object built from the input
720          * @throws IllegalStateException if bytes is bigger than [.MAX_DATA_BYTES]
721          */
722         @JvmStatic
723         @TypeConverter
724         fun fromByteArray(bytes: ByteArray): Data {
725             fun ByteArrayInputStream.isObjectStream(): Boolean {
726                 val header = ByteArray(2)
727                 read(header)
728                 val magic = ObjectStreamConstants.STREAM_MAGIC.toInt()
729                 val magicLow = magic.toByte()
730                 val magicHigh = (magic ushr 8).toByte()
731                 val result = (header[0] == magicHigh) && (header[1] == magicLow)
732                 reset()
733                 return result
734             }
735             fun DataInputStream.readHeader() {
736                 readShort().let { magic ->
737                     check(magic == STREAM_MAGIC) { "Magic number doesn't match: $magic" }
738                 }
739                 readShort().let { version ->
740                     check(version == STREAM_VERSION) { "Unsupported version number: $version" }
741                 }
742             }
743             fun DataInputStream.readValue(type: Byte): Any? {
744                 return when (type) {
745                     TYPE_NULL -> null
746                     TYPE_BOOLEAN -> readBoolean()
747                     TYPE_BYTE -> readByte()
748                     TYPE_INTEGER -> readInt()
749                     TYPE_LONG -> readLong()
750                     TYPE_FLOAT -> readFloat()
751                     TYPE_DOUBLE -> readDouble()
752                     TYPE_STRING -> readUTF()
753                     TYPE_BOOLEAN_ARRAY -> {
754                         Array(readInt()) { readBoolean() }
755                     }
756                     TYPE_BYTE_ARRAY -> {
757                         Array(readInt()) { readByte() }
758                     }
759                     TYPE_INTEGER_ARRAY -> {
760                         Array(readInt()) { readInt() }
761                     }
762                     TYPE_LONG_ARRAY -> {
763                         Array(readInt()) { readLong() }
764                     }
765                     TYPE_FLOAT_ARRAY -> {
766                         Array(readInt()) { readFloat() }
767                     }
768                     TYPE_DOUBLE_ARRAY -> {
769                         Array(readInt()) { readDouble() }
770                     }
771                     TYPE_STRING_ARRAY -> {
772                         Array(readInt()) {
773                             readUTF().let {
774                                 if (it == NULL_STRING_V1) {
775                                     null
776                                 } else {
777                                     it
778                                 }
779                             }
780                         }
781                     }
782                     else -> {
783                         throw IllegalStateException("Unsupported type $type")
784                     }
785                 }
786             }
787             check(bytes.size <= MAX_DATA_BYTES) {
788                 "Data cannot occupy more than $MAX_DATA_BYTES bytes when serialized"
789             }
790             if (bytes.isEmpty()) return EMPTY
791 
792             val map = mutableMapOf<String, Any?>()
793             try {
794                 ByteArrayInputStream(bytes).let { inputStream ->
795                     if (inputStream.isObjectStream()) { // V0
796                         ObjectInputStream(inputStream).use {
797                             it.apply { repeat(readInt()) { map[readUTF()] = readObject() } }
798                         }
799                     } else { // V1
800                         DataInputStream(inputStream).use {
801                             it.apply {
802                                 readHeader()
803                                 repeat(readInt()) {
804                                     val type = readByte()
805                                     val value = readValue(type)
806                                     val key = readUTF()
807                                     map[key] = value
808                                 }
809                             }
810                         }
811                     }
812                 }
813             } catch (e: IOException) {
814                 loge(TAG, e) { "Error in Data#fromByteArray: " }
815             } catch (e: ClassNotFoundException) {
816                 loge(TAG, e) { "Error in Data#fromByteArray: " }
817             }
818             return Data(map)
819         }
820     }
821 }
822 
convertPrimitiveArraynull823 private fun convertPrimitiveArray(value: BooleanArray): Array<Boolean> =
824     Array(value.size) { i -> value[i] }
825 
convertPrimitiveArraynull826 private fun convertPrimitiveArray(value: ByteArray): Array<Byte> =
827     Array(value.size) { i -> value[i] }
828 
convertPrimitiveArraynull829 private fun convertPrimitiveArray(value: IntArray): Array<Int> = Array(value.size) { i -> value[i] }
830 
convertPrimitiveArraynull831 private fun convertPrimitiveArray(value: LongArray): Array<Long> =
832     Array(value.size) { i -> value[i] }
833 
convertPrimitiveArraynull834 private fun convertPrimitiveArray(value: FloatArray): Array<Float> =
835     Array(value.size) { i -> value[i] }
836 
convertPrimitiveArraynull837 private fun convertPrimitiveArray(value: DoubleArray): Array<Double> =
838     Array(value.size) { i -> value[i] }
839 
840 private val TAG = Logger.tagWithPrefix("Data")
841