-
Notifications
You must be signed in to change notification settings - Fork 76
/
ByteUtils.scala
217 lines (187 loc) · 6.24 KB
/
ByteUtils.scala
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package io.iohk.ethereum.utils
import java.math.BigInteger
import java.nio.{ByteBuffer, ByteOrder}
import akka.util.ByteString
import scala.util.Random
object ByteUtils {
/**
* Calculates number of matching bytes from the beginning of both arrays.
* Due to performance reasons needs to be as fast as possible which means usage of while loops and var's.
*
* @param a - first array of bytes to check
* @param b - second array to bytes to check
* @return Length of common prefix shared by both arrays
*/
def matchingLength(a: Array[Byte], b: Array[Byte]): Int = {
var prefixLen = 0
while (prefixLen < a.length && prefixLen < b.length && a(prefixLen) == b(prefixLen)) {
prefixLen = prefixLen + 1
}
prefixLen
}
def bigIntegerToBytes(b: BigInteger, numBytes: Int): Array[Byte] = {
val bytes = new Array[Byte](numBytes)
val biBytes = b.toByteArray
val start = if (biBytes.length == numBytes + 1) 1 else 0
val length = Math.min(biBytes.length, numBytes)
System.arraycopy(biBytes, start, bytes, numBytes - length, length)
bytes
}
def toBigInt(bytes: ByteString): BigInt =
bytes.foldLeft(BigInt(0)){(n, b) => (n << 8) + (b & 0xff)}
/**
* Calculates xor distance between two byte arrays. Due to performance reasons needs to be as fast as possible
* which means usage of while loops and var's.
*
* @param a - array of bytes to xor
* @param b - array of bytes to xor
* @return Array[Byte] - each element of array is equal to `(a(i) ^ b(i))`
*/
def xor(a: Array[Byte], b: Array[Byte]): Array[Byte] = {
val ret = new Array[Byte](a.length)
var i = 0
while (i < a.length) {
ret(i) = (a(i) ^ b(i)).toByte
i += 1
}
ret
}
def or(arrays: Array[Byte]*): Array[Byte] = {
require(arrays.map(_.length).distinct.length <= 1, "All the arrays should have the same length")
require(arrays.nonEmpty, "There should be one or more arrays")
val zeroes = Array.fill(arrays.head.length)(0.toByte)
arrays.foldLeft[Array[Byte]](zeroes){
case (prevOr, array) => prevOr.zip(array).map{ case (b1, b2) => (b1 | b2).toByte }
}
}
def and(arrays: Array[Byte]*): Array[Byte] = {
require(arrays.map(_.length).distinct.length <= 1, "All the arrays should have the same length")
require(arrays.nonEmpty, "There should be one or more arrays")
val ones = Array.fill(arrays.head.length)(0xFF.toByte)
arrays.foldLeft[Array[Byte]](ones){
case (prevOr, array) => prevOr.zip(array).map{ case (b1, b2) => (b1 & b2).toByte }
}
}
def randomBytes(len: Int): Array[Byte] = {
val arr = new Array[Byte](len)
new Random().nextBytes(arr)
arr
}
def bigEndianToShort(bs: Array[Byte]): Short = {
val n = bs(0) << 8
(n | bs(1) & 0xFF).toShort
}
def padLeft(bytes: ByteString, length: Int, byte: Byte = 0): ByteString = {
val l = math.max(0, length - bytes.length)
val fill = Seq.fill[Byte](l)(byte)
fill ++: bytes
}
def compactPickledBytesToArray(buffer: ByteBuffer): Array[Byte] = {
val data = Array.ofDim[Byte](buffer.limit())
buffer.rewind()
buffer.get(data)
data
}
def compactPickledBytes(buffer: ByteBuffer): ByteString = {
ByteString(compactPickledBytesToArray(buffer))
}
def bytesToInts(bytes: Array[Byte], bigEndian: Boolean): Array[Int] = {
val ret = new Array[Int](bytes.length / 4)
bytesToIntsMut(bytes, ret, bigEndian)
ret
}
def intsToBytes(ints: Array[Int], bigEndian: Boolean): Array[Byte] = {
val ret = new Array[Byte](ints.length * 4)
intsToBytesMut(ints, ret, bigEndian)
ret
}
def getIntFromWord(arr: Array[Byte]): Int = {
ByteBuffer.wrap(arr, 0, 4).order(ByteOrder.LITTLE_ENDIAN).getInt
}
/**
* Converts array of Int to corresponding array of bytes. Due to performance reasons needs to be as fast as possible
* which means usage of while loops and var's.
*
* @param arr - array of int's to convert
* @param b - array for resulting byte conversion. It will be mutated in place, and it's length needs to be equal to
* `(arr.length * 4)`
* @param bigEndian - param specifying which int representation should be used.
* @return Unit
*/
def intsToBytesMut(arr: Array[Int], b: Array[Byte], bigEndian: Boolean) {
if (!bigEndian) {
var off = 0
var i = 0
while (i < arr.length) {
val ii = arr(i)
b(off) = (ii & 0xFF).toByte
off += 1
b(off) = ((ii >> 8) & 0xFF).toByte
off += 1
b(off)= ((ii >> 16) & 0xFF).toByte
off += 1
b(off) = ((ii >> 24) & 0xFF).toByte
off += 1
i = i + 1
}
} else {
var off = 0
var i = 0
while (i < arr.length) {
val ii = arr(i)
b(off) = ((ii >> 24) & 0xFF).toByte
off += 1
b(off) = ((ii >> 16) & 0xFF).toByte
off += 1
b(off)= ((ii >> 8) & 0xFF).toByte
off += 1
b(off) = (ii & 0xFF).toByte
off += 1
i = i + 1
}
}
}
/**
* Converts array of bytes to corresponding array of ints. Due to performance reasons needs to be as fast as possible
* which means usage of while loops and var's.
*
* @param b - array of bytes to convert
* @param arr - array for resulting int conversion. It will be mutated in place, and it's length needs to be equal to
* `(b.length / 4)`
* @param bigEndian - param specifying which int representation should be used.
* @return Unit
*/
def bytesToIntsMut(b: Array[Byte], arr: Array[Int], bigEndian: Boolean) {
if (!bigEndian) {
var off = 0
var i = 0
while (i < arr.length) {
var ii: Int = b(off) & 0x000000FF
off += 1
ii |= (b(off) << 8) & 0x0000FF00
off += 1
ii |= (b(off) << 16) & 0x00FF0000
off += 1
ii |= (b(off) << 24)
off += 1
arr(i) = ii
i = i + 1
}
} else {
var off = 0
var i = 0
while (i < arr.length) {
var ii: Int = b(off) << 24
off += 1
ii |= (b(off) << 16) & 0x00FF0000
off += 1
ii |= (b(off) << 8) & 0x0000FF00
off += 1
ii |= b(off) & 0x000000FF
off += 1
arr(i) = ii
i = i + 1
}
}
}
}