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