I wrote a function to decode GSM 7-Bit encoded messages as defined in the 3GPP standard. Example available here. I found a tool to help me check my answers here. I used the charset from here.
public String decode(final byte[] data) {
if (data == null) {
return null;
}
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (int i = 0, counter = 0; i < data.length; i++, counter++) {
final byte current = data[i];
final byte previous = i == 0 ? 0 : data[i - 1];
if (counter == 7) {
counter = 0;
}
final int shiftedCurrent = (current << counter) & 0x7F;
final int shiftedPrevious = (previous & 0xFF) >> (8 - counter);
bos.write(shiftedCurrent | shiftedPrevious);
if (counter == 6) {
bos.write((current & 0xFF) >> 1);
}
}
try {
final String result = bos.toString("X-Gsm7Bit");
return result.endsWith("\r") ? result.substring(0, result.length() - 1) : result;
} catch (final UnsupportedEncodingException e) {
throw new AsnDecodeException("Cannot get charset: X-Gsm7Bit");
}
}
Tests:
@Test
public void decodeTests() throws DecoderException {
assertThat(decode(Hex.decodeHex("f4f29c9e769f1b".toCharArray()))).isEqualTo("testing");
assertThat(decode(Hex.decodeHex("31D98C56B3DD70".toCharArray()))).isEqualTo("12345678");
assertThat(decode(Hex.decodeHex("6176D94DAFCB1B".toCharArray()))).isEqualTo("alentur");
assertThat(decode(Hex.decodeHex("61F79B8E2ECB5B657CB80D679701E77638CD768DDF6D".toCharArray()))).isEqualTo("[email protected]");
}
I feel like I could have done this with fewer shifts or &
checks, but the thing I really don't like is my counter variable that I reset at 7. Any advice on how to improve this code?