package decoder.primitive;
import api.Decoder;
import api.exceptions.DecodingException;
import org.apache.commons.codec.binary.Hex;
import java.io.IOException;
import java.io.InputStream;
/**
* If you see it, than I've forgotten javadoc
*
* @author Denis Golovachev
* @author $Author$ (current maintainer)
* @since 1.0
*/
public class HexStringDecoder implements Decoder<String> {
private static final int constructor = ChecksumCalculator.calculate("string ? = String");
private static final int longStringMarker = 0xFE;
/**
* Наконец, значения типа string выглядят по-разному в зависимости от длины передаваемой строки L.
* Если L <= 253, то кодируется один байт L, затем L байтов строки, затем от 0 до 3 символов с кодом 0,
* чтобы общая длина значения делилась на 4, после чего все это интерпретируется как последовательность
* из int(L/4)+1 32-битных чисел. Если же L>=254, то кодируется байт 254, затем — 3 байта с длиной строки L,
* затем — L байтов строки, затем - от 0 до 3 нулевых байтов выравнивания.
*/
@Override
public String decode(InputStream stream) throws DecodingException {
try {
int firstByte = stream.read();
if(firstByte == longStringMarker) {
return read(stream);
}
return read(stream, firstByte);
} catch (IOException e) {
throw new DecodingException("Do not like InputStream interface for ugly checked exception");
}
}
/**
* Long strings contains 'size' in three bytes, after 0xFE(first byte)
* @param dataStream
* @return
* @throws IOException
*/
private String read(InputStream dataStream) throws IOException {
byte[] lengthBytes = new byte[3];
dataStream.read(lengthBytes);
int length = lengthBytes[0] + lengthBytes[1] * 256 + lengthBytes[2] * 65536;
return read(dataStream, length);
}
private String read(InputStream stream, int size) throws IOException {
byte[] stringBytes = new byte[size];
stream.read(stringBytes);
removePadding(stream, size + 1); // +1 byte = length byte.
return Hex.encodeHexString(stringBytes);
}
private void removePadding(InputStream stream, int size) throws IOException {
int difference = (size + 4) % 4;
int padding = difference > 0 ? 4 - difference: 0; // 32 bit padding
stream.read(new byte[padding]); // padding is useless
}
@Override
public boolean isTypeConstructor(int otherConstructor) {
return constructor == otherConstructor;
}
}