How to convert signed 16 bit integer to unsigned 1

2019-08-09 06:42发布

问题:

I have my below layout in which I need to represent my data and then finally I need to make one byte array out of that. I need to represent my data in the below format from Java code and then send the byte array to my C++ program which in turns c++ program unpacks the byte array and extract the relevant stuff from it -

// below is my data layout -
//
// key type - 1 byte
// key len - 1 byte
// key (variable size = key_len)
// timestamp (sizeof uint64_t)
// data size (sizeof uint16_t), this is unsigned 16-bit integer.
// data (variable size = data size)

So I started like this in Java which works fine on a simple use case which makes a single byte array out of this but I am thinking my dataSize calculation is wrong since I am using Short for the dataSize which can take maximum value as 32767 but dataSize specification is uint16_t as shown above which is unsigned 16-bit integer which can take maximum value more than 32767.

byte keyType = 101;
byte keyLength = 3;
byte[] key = {27, 55, 111};
long timestamp = System.currentTimeMillis();

byte[] data = "string data".getBytes("UTF-8");

// this is looking wrong to me since dataSize is uint16_t in my C++ specifications as shown above
short dataSize = (short) data.length;

int totalSize = (1 + 1 + keyLength + 8 + 2 + dataSize);
ByteBuffer bytes = ByteBuffer.allocate(totalSize);

bytes.put(keyType);
bytes.put(keyLength);
bytes.put(key);
bytes.putLong(timestamp);

// so this is also wrong
// what is the right way to send the dataSize?
bytes.putShort(dataSize);
bytes.put(data);

// write everthing as a single byte array:
byte[] byteArray = bytes.array();

Let's say if the length of data is 37714 (data.length), then dataSize will come as negative -27822.

So my question is - Is there any way I can have unsigned 16 bit Integer in java which I can use in my above code or some way to cast it?

Does my above code looks right with the above specifications or is there anything wrong which might cause problem if we have some big strings coming up.

回答1:

Java's only unsigned type is char, and it happens to be 16-bit wide. However, this is not the right choice to represent length: you should use int internally, and convert to/from 16-bit short only during serialization and deserialization.

int dataSize = data.length;
...
bytes.putShort((short)(dataSize & 0xFFFF));

and

short tmp = getShort(...);
int dataSize = ((int)tmp) & 0xFFFF;

0xFFFF is a 16-bit "mask" that cuts off the undesired bits of an int. Note that if tmp happens to be negative during deserialization, masking will make it positive again, producing the value that has been sent to you.