Skip to content

Commit

Permalink
fixed a bug when reading 7bit encoded strings
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkAtra committed Jun 9, 2021
1 parent 7bf4a40 commit 4a0d191
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 6 deletions.
12 changes: 6 additions & 6 deletions core/src/main/kotlin/de/darkatra/bfme2/InputStreamExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ fun InputStream.read7BitString(): String {
val maxBytesWithoutOverflow = 4

for (shift in 0 until maxBytesWithoutOverflow * 7 step 7) {
val byte = this.readByte()
result = result or (byte and 0b01111111.toByte()).toInt() shl shift
val byte = this.readByte().toUByte()
result = result or (byte and 0b01111111.toUByte()).toInt() shl shift

// exit early if the byte's int value is not bigger than 127 (meaning the msb is not 1)
if (byte <= 0b01111111.toByte()) {
if (byte <= 0b01111111.toUByte()) {
return result
}
}

// read the 5th byte. Since we already read 28 bits, the value of this byte must fit within 4 bits (32 - 28)
// msb should not be set to 1
val byte = this.readByte()
if (byte > 0b1111.toByte()) {
val byte = this.readByte().toUByte()
if (byte > 0b1111.toUByte()) {
throw NumberFormatException("Could not read 7bit encoded Int. 5th byte had more than 4 set bits.")
}
return result or (byte and 0b01111111.toByte()).toInt() shl (maxBytesWithoutOverflow * 7)
return result or (byte and 0b01111111.toUByte()).toInt() shl (maxBytesWithoutOverflow * 7)
}

// read the 7 bit encoded int to determine the length of the string
Expand Down
15 changes: 15 additions & 0 deletions core/src/main/kotlin/de/darkatra/bfme2/OutputStreamExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,18 @@ fun OutputStream.writeUShortPrefixedString(string: String, charset: Charset = St
this.writeUShort(stringLength.toUShort())
this.write(string.toByteArray(charset))
}

fun OutputStream.write7BitString(string: String) {

val bytesToWrite = string.toByteArray(StandardCharsets.UTF_8)

var uInt = bytesToWrite.size.toUInt()

while (uInt > Byte.MAX_VALUE.toUInt()) {
this.writeByte(uInt.or(Byte.MIN_VALUE.toUInt()).toByte())
uInt = uInt.shr(7)
}
this.writeByte(uInt.toByte())

this.write(bytesToWrite)
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,45 @@ internal class OutputStreamExtensionsTest {
outputStream.writeUShortPrefixedString(extremelyLongString)
}
}

@Test
internal fun shouldWrite7BitString() {

val outputStream = ByteArrayOutputStream()

val testString = (0 until 100).joinToString("") { "a" }

outputStream.write7BitString(testString)

assertThat(outputStream.toByteArray()).isEqualTo(byteArrayOf(
100, *(0 until 100).map { 97.toByte() }.toByteArray()
))
}

@Test
internal fun shouldWrite7BitStringWithLengthOf200Characters() {

val outputStream = ByteArrayOutputStream()

val testString = (0 until 200).joinToString("") { "a" }

outputStream.write7BitString(testString)

assertThat(outputStream.toByteArray()).isEqualTo(byteArrayOf(
200.toUByte().toByte(), 1, *(0 until 200).map { 97.toByte() }.toByteArray()
))
}

@Test
internal fun shouldRoundtrip7BitString() {

val outputStream = ByteArrayOutputStream()

val testString = (0 until 200).joinToString("") { "a" }

outputStream.write7BitString(testString)

assertThat(outputStream.toByteArray().inputStream().read7BitString()).isEqualTo(testString)
}
}
}

0 comments on commit 4a0d191

Please sign in to comment.