Skip to content

Commit 0cff4ca

Browse files
authored
Read/Write Buffer improvements (#818)
* Split buffer and array readers and writers, support foreign character char reading, optimise string reading, support reading LongArray, IntArray, ShortArray, ByteArray, FloatArray, DoubleArray closes #473 * Fix readBytes with offset
1 parent 587d4c1 commit 0cff4ca

File tree

70 files changed

+1062
-490
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1062
-490
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package world.gregs.voidps.buffer
2+
3+
object Unicode {
4+
fun byteToChar(b: Int): Int {
5+
var i = 0xff and b
6+
require(i != 0) { "Non cp1252 character 0x" + i.toString(16) + " provided" }
7+
if (i in 128..159) {
8+
var char = table[i - 128].code
9+
if (char == 0) {
10+
char = 63
11+
}
12+
i = char
13+
}
14+
return i
15+
}
16+
17+
fun charToByte(c: Char) = when (val code = c.code) {
18+
63 -> throw IllegalArgumentException("Cannot map '?' back to a specific byte")
19+
in 0..127 -> code.toByte().toInt()
20+
in 160..255 -> code.toByte().toInt()
21+
else -> {
22+
val indexInTable = table.indexOfFirst { it.code == code }
23+
if (indexInTable == -1) {
24+
throw IllegalArgumentException("Char '$c' (0x${code.toString(16)}) not in CP1252 mapping")
25+
}
26+
(128 + indexInTable).toByte().toInt()
27+
}
28+
}
29+
30+
private val table = charArrayOf(
31+
'\u20ac',
32+
'\u0000',
33+
'\u201a',
34+
'\u0192',
35+
'\u201e',
36+
'\u2026',
37+
'\u2020',
38+
'\u2021',
39+
'\u02c6',
40+
'\u2030',
41+
'\u0160',
42+
'\u2039',
43+
'\u0152',
44+
'\u0000',
45+
'\u017d',
46+
'\u0000',
47+
'\u0000',
48+
'\u2018',
49+
'\u2019',
50+
'\u201c',
51+
'\u201d',
52+
'\u2022',
53+
'\u2013',
54+
'\u2014',
55+
'\u02dc',
56+
'\u2122',
57+
'\u0161',
58+
'\u203a',
59+
'\u0153',
60+
'\u0000',
61+
'\u017e',
62+
'\u0178',
63+
)
64+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package world.gregs.voidps.buffer.read
2+
3+
import java.nio.ByteBuffer
4+
5+
class ArrayReader(
6+
array: ByteArray = ByteArray(0)
7+
) : Reader {
8+
var array: ByteArray = array
9+
private set
10+
11+
constructor(buffer: ByteBuffer) : this(buffer.array())
12+
13+
override var length = array.size
14+
var position = 0
15+
override val remaining: Int
16+
get() = length - position
17+
private var bitIndex = 0
18+
19+
fun set(array: ByteArray) {
20+
position = 0
21+
bitIndex = 0
22+
length = array.size
23+
this.array = array
24+
}
25+
26+
override fun peek(): Int = array[position].toInt()
27+
28+
override fun readByte(): Int = array[position++].toInt()
29+
30+
override fun readString(): String {
31+
val start = position
32+
var pos = start
33+
while (array[pos] != 0.toByte()) {
34+
pos++
35+
}
36+
val length = pos - start
37+
position(position() + length + 1)
38+
return String(array, start, length, Charsets.UTF_8)
39+
}
40+
41+
override fun readBytes(value: ByteArray) {
42+
System.arraycopy(array, position, value, 0, value.size)
43+
position += value.size
44+
}
45+
46+
override fun readBytes(value: ShortArray) {
47+
ByteBuffer.wrap(array, position, value.size * 2)
48+
.asShortBuffer()
49+
.get(value)
50+
position += value.size * 2
51+
}
52+
53+
override fun readBytes(value: IntArray) {
54+
ByteBuffer.wrap(array, position, value.size * 4)
55+
.asIntBuffer()
56+
.get(value)
57+
position += value.size * 4
58+
}
59+
60+
override fun readBytes(value: LongArray) {
61+
ByteBuffer.wrap(array, position, value.size * 8)
62+
.asLongBuffer()
63+
.get(value)
64+
position += value.size * 8
65+
}
66+
67+
override fun readBytes(value: FloatArray) {
68+
ByteBuffer.wrap(array, position, value.size * 4)
69+
.asFloatBuffer()
70+
.get(value)
71+
position += value.size * 4
72+
}
73+
74+
override fun readBytes(value: DoubleArray) {
75+
ByteBuffer.wrap(array, position, value.size * 8)
76+
.asDoubleBuffer()
77+
.get(value)
78+
position += value.size * 8
79+
}
80+
81+
override fun readBytes(array: ByteArray, offset: Int, length: Int) {
82+
System.arraycopy(this.array, position, array, offset, length)
83+
position += length
84+
}
85+
86+
override fun skip(amount: Int) {
87+
position += amount
88+
}
89+
90+
override fun position(): Int = position
91+
92+
override fun position(index: Int) {
93+
position = index
94+
}
95+
96+
override fun array(): ByteArray = array
97+
98+
override fun readableBytes(): Int = remaining
99+
100+
override fun startBitAccess(): Reader {
101+
bitIndex = position() * 8
102+
return this
103+
}
104+
105+
override fun stopBitAccess(): Reader {
106+
position((bitIndex + 7) / 8)
107+
return this
108+
}
109+
110+
@Suppress("NAME_SHADOWING")
111+
override fun readBits(bitCount: Int): Int {
112+
if (bitCount !in 0..32) {
113+
throw IllegalArgumentException("Number of bits must be between 1 and 32 inclusive")
114+
}
115+
116+
var bitCount = bitCount
117+
var bytePos = bitIndex shr 3
118+
var bitOffset = 8 - (bitIndex and 7)
119+
var value = 0
120+
bitIndex += bitCount
121+
122+
while (bitCount > bitOffset) {
123+
value += array[bytePos++].toInt() and BIT_MASKS[bitOffset] shl bitCount - bitOffset
124+
bitCount -= bitOffset
125+
bitOffset = 8
126+
}
127+
value += if (bitCount == bitOffset) {
128+
array[bytePos].toInt() and BIT_MASKS[bitOffset]
129+
} else {
130+
array[bytePos].toInt() shr bitOffset - bitCount and BIT_MASKS[bitCount]
131+
}
132+
return value
133+
}
134+
135+
companion object {
136+
/**
137+
* Bit masks for [readBits]
138+
*/
139+
private val BIT_MASKS = IntArray(32)
140+
141+
init {
142+
for (i in BIT_MASKS.indices) {
143+
BIT_MASKS[i] = (1 shl i) - 1
144+
}
145+
}
146+
}
147+
}

buffer/src/main/kotlin/world/gregs/voidps/buffer/read/BufferReader.kt

Lines changed: 60 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -11,96 +11,82 @@ class BufferReader(
1111
override val length: Int = buffer.remaining()
1212
override val remaining: Int
1313
get() = buffer.remaining()
14-
private var bitIndex = 0
15-
16-
override fun readByte(): Int = buffer.get().toInt()
17-
18-
override fun readByteAdd(): Int = (readByte() - 128).toByte().toInt()
19-
20-
override fun readByteInverse(): Int = -readByte()
21-
22-
override fun readByteSubtract(): Int = (readByteInverse() + 128).toByte().toInt()
23-
24-
override fun readUnsignedByte(): Int = readByte() and 0xff
25-
26-
override fun readShort(): Int = (readByte() shl 8) or readUnsignedByte()
27-
28-
override fun readShortAdd(): Int = (readByte() shl 8) or readUnsignedByteAdd()
2914

30-
override fun readUnsignedShortAdd(): Int = (readByte() shl 8) or ((readByte() - 128) and 0xff)
31-
32-
override fun readShortLittle(): Int = readUnsignedByte() or (readByte() shl 8)
33-
34-
override fun readShortAddLittle(): Int = readUnsignedByteAdd() or (readByte() shl 8)
35-
36-
override fun readUnsignedByteAdd(): Int = (readByte() - 128).toByte().toInt()
37-
38-
override fun readUnsignedShort(): Int = (readUnsignedByte() shl 8) or readUnsignedByte()
39-
40-
override fun readUnsignedShortLittle(): Int = readUnsignedByte() or (readUnsignedByte() shl 8)
41-
42-
override fun readMedium(): Int = (readByte() shl 16) or (readByte() shl 8) or readUnsignedByte()
43-
44-
override fun readUnsignedMedium(): Int = (readUnsignedByte() shl 16) or (readUnsignedByte() shl 8) or readUnsignedByte()
15+
override fun peek(): Int {
16+
return buffer.get(buffer.position()).toInt()
17+
}
4518

46-
override fun readInt(): Int = (readUnsignedByte() shl 24) or (readUnsignedByte() shl 16) or (readUnsignedByte() shl 8) or readUnsignedByte()
19+
private var bitIndex = 0
4720

48-
override fun readIntInverseMiddle(): Int = (readByte() shl 16) or (readByte() shl 24) or readUnsignedByte() or (readByte() shl 8)
21+
override fun readByte(): Int = buffer.get().toInt()
4922

50-
override fun readIntLittle(): Int = readUnsignedByte() or (readByte() shl 8) or (readByte() shl 16) or (readByte() shl 24)
23+
override fun readString(): String {
24+
if (buffer.hasArray()) {
25+
val array = buffer.array()
26+
val offset = buffer.arrayOffset() + position()
27+
val start = offset
28+
var pos = offset
29+
while (array[pos] != 0.toByte()) {
30+
pos++
31+
}
32+
val length = pos - start
33+
position(position() + length + 1)
34+
return String(array, start, length, Charsets.UTF_8)
35+
} else {
36+
// Fallback for direct buffers
37+
val start = position()
38+
var pos = start
39+
while (buffer.get(pos) != 0.toByte()) {
40+
pos++
41+
}
5142

52-
override fun readUnsignedIntMiddle(): Int = (readUnsignedByte() shl 8) or readUnsignedByte() or (readUnsignedByte() shl 24) or (readUnsignedByte() shl 16)
43+
val length = pos - start
44+
val bytes = ByteArray(length)
45+
buffer.position(start)
46+
buffer.get(bytes)
47+
buffer.position(pos + 1)
5348

54-
override fun readSmart(): Int {
55-
val peek = readUnsignedByte()
56-
return if (peek < 128) {
57-
peek
58-
} else {
59-
(peek shl 8 or readUnsignedByte()) - 32768
49+
return String(bytes, Charsets.UTF_8)
6050
}
6151
}
6252

63-
override fun readBigSmart(): Int {
64-
val peek = readByte()
65-
return if (peek < 0) {
66-
((peek shl 24) or (readUnsignedByte() shl 16) or (readUnsignedByte() shl 8) or readUnsignedByte()) and 0x7fffffff
67-
} else {
68-
val value = (peek shl 8) or readUnsignedByte()
69-
if (value == 32767) -1 else value
70-
}
53+
override fun readBytes(value: ByteArray) {
54+
buffer.get(value)
7155
}
7256

73-
override fun readLargeSmart(): Int {
74-
var baseValue = 0
75-
var lastValue = readSmart()
76-
while (lastValue == 32767) {
77-
lastValue = readSmart()
78-
baseValue += 32767
79-
}
80-
return baseValue + lastValue
57+
override fun readBytes(value: ShortArray) {
58+
buffer
59+
.asShortBuffer()
60+
.get(value)
61+
buffer.position(buffer.position() + value.size * 2)
8162
}
8263

83-
override fun readLong(): Long {
84-
val first = readInt().toLong() and 0xffffffffL
85-
val second = readInt().toLong() and 0xffffffffL
86-
return second + (first shl 32)
64+
override fun readBytes(value: IntArray) {
65+
buffer
66+
.asIntBuffer()
67+
.get(value)
68+
buffer.position(buffer.position() + value.size * 4)
8769
}
8870

89-
override fun readString(): String {
90-
val sb = StringBuilder()
91-
var b: Int
92-
while (buffer.hasRemaining()) {
93-
b = readUnsignedByte()
94-
if (b == 0) {
95-
break
96-
}
97-
sb.append(b.toChar())
98-
}
99-
return sb.toString()
71+
override fun readBytes(value: LongArray) {
72+
buffer
73+
.asLongBuffer()
74+
.get(value)
75+
buffer.position(buffer.position() + value.size * 8)
10076
}
10177

102-
override fun readBytes(value: ByteArray) {
103-
buffer.get(value)
78+
override fun readBytes(value: FloatArray) {
79+
buffer
80+
.asFloatBuffer()
81+
.get(value)
82+
buffer.position(buffer.position() + value.size * 4)
83+
}
84+
85+
override fun readBytes(value: DoubleArray) {
86+
buffer
87+
.asDoubleBuffer()
88+
.get(value)
89+
buffer.position(buffer.position() + value.size * 8)
10490
}
10591

10692
override fun readBytes(array: ByteArray, offset: Int, length: Int) {
@@ -133,7 +119,7 @@ class BufferReader(
133119

134120
@Suppress("NAME_SHADOWING")
135121
override fun readBits(bitCount: Int): Int {
136-
if (bitCount < 0 || bitCount > 32) {
122+
if (bitCount !in 0..32) {
137123
throw IllegalArgumentException("Number of bits must be between 1 and 32 inclusive")
138124
}
139125

0 commit comments

Comments
 (0)