aboutsummaryrefslogtreecommitdiff
path: root/okio/src/commonMain/kotlin/okio/Util.kt
blob: bfd8fec1b2ac1f0e8b9935c71c4713495c239d33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
 * Copyright (C) 2018 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
@file:JvmName("-SegmentedByteString") // A leading '-' hides this class from Java.

package okio

import kotlin.jvm.JvmName
import kotlin.native.concurrent.SharedImmutable
import okio.internal.HEX_DIGIT_CHARS

internal fun checkOffsetAndCount(size: Long, offset: Long, byteCount: Long) {
  if (offset or byteCount < 0 || offset > size || size - offset < byteCount) {
    throw ArrayIndexOutOfBoundsException("size=$size offset=$offset byteCount=$byteCount")
  }
}

/* ktlint-disable no-multi-spaces indent */

internal fun Short.reverseBytes(): Short {
  val i = toInt() and 0xffff
  val reversed = (i and 0xff00 ushr 8) or
    (i and 0x00ff  shl 8)
  return reversed.toShort()
}

internal fun Int.reverseBytes(): Int {
  return (this and -0x1000000 ushr 24) or
    (this and 0x00ff0000 ushr  8) or
    (this and 0x0000ff00  shl  8) or
    (this and 0x000000ff  shl 24)
}

internal fun Long.reverseBytes(): Long {
  return (this and -0x100000000000000L ushr 56) or
    (this and 0x00ff000000000000L ushr 40) or
    (this and 0x0000ff0000000000L ushr 24) or
    (this and 0x000000ff00000000L ushr  8) or
    (this and 0x00000000ff000000L  shl  8) or
    (this and 0x0000000000ff0000L  shl 24) or
    (this and 0x000000000000ff00L  shl 40) or
    (this and 0x00000000000000ffL  shl 56)
}

/* ktlint-enable no-multi-spaces indent */

internal inline infix fun Int.leftRotate(bitCount: Int): Int {
  return (this shl bitCount) or (this ushr (32 - bitCount))
}

internal inline infix fun Long.rightRotate(bitCount: Int): Long {
  return (this ushr bitCount) or (this shl (64 - bitCount))
}

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline infix fun Byte.shr(other: Int): Int = toInt() shr other

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline infix fun Byte.shl(other: Int): Int = toInt() shl other

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline infix fun Byte.and(other: Int): Int = toInt() and other

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline infix fun Byte.and(other: Long): Long = toLong() and other

@Suppress("NOTHING_TO_INLINE") // Pending `kotlin.experimental.xor` becoming stable
internal inline infix fun Byte.xor(other: Byte): Byte = (toInt() xor other.toInt()).toByte()

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline infix fun Int.and(other: Long): Long = toLong() and other

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline fun minOf(a: Long, b: Int): Long = minOf(a, b.toLong())

@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
internal inline fun minOf(a: Int, b: Long): Long = minOf(a.toLong(), b)

internal fun arrayRangeEquals(
  a: ByteArray,
  aOffset: Int,
  b: ByteArray,
  bOffset: Int,
  byteCount: Int,
): Boolean {
  for (i in 0 until byteCount) {
    if (a[i + aOffset] != b[i + bOffset]) return false
  }
  return true
}

internal fun Byte.toHexString(): String {
  val result = CharArray(2)
  result[0] = HEX_DIGIT_CHARS[this shr 4 and 0xf]
  result[1] = HEX_DIGIT_CHARS[this       and 0xf] // ktlint-disable no-multi-spaces
  return result.concatToString()
}

internal fun Int.toHexString(): String {
  if (this == 0) return "0" // Required as code below does not handle 0

  val result = CharArray(8)
  result[0] = HEX_DIGIT_CHARS[this shr 28 and 0xf]
  result[1] = HEX_DIGIT_CHARS[this shr 24 and 0xf]
  result[2] = HEX_DIGIT_CHARS[this shr 20 and 0xf]
  result[3] = HEX_DIGIT_CHARS[this shr 16 and 0xf]
  result[4] = HEX_DIGIT_CHARS[this shr 12 and 0xf]
  result[5] = HEX_DIGIT_CHARS[this shr 8  and 0xf] // ktlint-disable no-multi-spaces
  result[6] = HEX_DIGIT_CHARS[this shr 4  and 0xf] // ktlint-disable no-multi-spaces
  result[7] = HEX_DIGIT_CHARS[this        and 0xf] // ktlint-disable no-multi-spaces

  // Find the first non-zero index
  var i = 0
  while (i < result.size) {
    if (result[i] != '0') break
    i++
  }

  return result.concatToString(i, result.size)
}

internal fun Long.toHexString(): String {
  if (this == 0L) return "0" // Required as code below does not handle 0

  val result = CharArray(16)
  result[ 0] = HEX_DIGIT_CHARS[(this shr 60 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 1] = HEX_DIGIT_CHARS[(this shr 56 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 2] = HEX_DIGIT_CHARS[(this shr 52 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 3] = HEX_DIGIT_CHARS[(this shr 48 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 4] = HEX_DIGIT_CHARS[(this shr 44 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 5] = HEX_DIGIT_CHARS[(this shr 40 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 6] = HEX_DIGIT_CHARS[(this shr 36 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 7] = HEX_DIGIT_CHARS[(this shr 32 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 8] = HEX_DIGIT_CHARS[(this shr 28 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[ 9] = HEX_DIGIT_CHARS[(this shr 24 and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[10] = HEX_DIGIT_CHARS[(this shr 20 and 0xf).toInt()]
  result[11] = HEX_DIGIT_CHARS[(this shr 16 and 0xf).toInt()]
  result[12] = HEX_DIGIT_CHARS[(this shr 12 and 0xf).toInt()]
  result[13] = HEX_DIGIT_CHARS[(this shr 8  and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[14] = HEX_DIGIT_CHARS[(this shr 4  and 0xf).toInt()] // ktlint-disable no-multi-spaces
  result[15] = HEX_DIGIT_CHARS[(this        and 0xf).toInt()] // ktlint-disable no-multi-spaces

  // Find the first non-zero index
  var i = 0
  while (i < result.size) {
    if (result[i] != '0') break
    i++
  }

  return result.concatToString(i, result.size)
}

// Work around a problem where Kotlin/JS IR can't handle default parameters on expect functions
// that depend on the receiver. We use well-known, otherwise-impossible values here and must check
// for them in the receiving function, then swap in the true default value.
// https://youtrack.jetbrains.com/issue/KT-45542

@SharedImmutable
internal val DEFAULT__new_UnsafeCursor = Buffer.UnsafeCursor()
internal fun resolveDefaultParameter(unsafeCursor: Buffer.UnsafeCursor): Buffer.UnsafeCursor {
  if (unsafeCursor === DEFAULT__new_UnsafeCursor) return Buffer.UnsafeCursor()
  return unsafeCursor
}

internal val DEFAULT__ByteString_size = -1234567890
internal fun ByteString.resolveDefaultParameter(position: Int): Int {
  if (position == DEFAULT__ByteString_size) return size
  return position
}

internal fun ByteArray.resolveDefaultParameter(sizeParam: Int): Int {
  if (sizeParam == DEFAULT__ByteString_size) return size
  return sizeParam
}