Putting a ByteBuffer into a Bundle - java

I'm working with some code that I have no control over, and the ByteBuffer I'm working with gets passed to native method. I don't have access to the native code but it expects "buf" to be a ByteBuffer. Also note that the code doesn't really make any sense but there is a lot so I am distilling it down to the issue.
public class otherClass {
public final void setParams(Bundle params) {
final String key = params.keySet()[0];
Object buf = params.get(key));
nativeSet(key, buf);
}
private native final void nativeSet(key, buf);
}
Here is my code:
public void myMethod(ByteBuffer myBuffer) {
final Bundle myBundle = new Bundle();
myBundle.putByteBuffer("param", myBuffer);
otherClass.setParams(runTimeParam);
}
The problem? There is no putByteBuffer method in Bundle. Seems kind of weird that there is a get() that returns an object, but no generic put().
But what seems weirder to me is that the native code wants a ByteBuffer. When it gets passed from Java layer, won't it have a bunch of metadata with it? Can code in the native layer predict the metadata and extract from the ByteBuffer?
Is there any way to reliably pass a ByteBuffer here? It can be a little hacky. I was thinking maybe I could figure out what the ByteBuffer object would be in bits, convert to integer, and use putInt(). Not sure how to go from ByteBuffer object to raw data.

Hypothetically this should work. Turn the byte buffer to a string and pass that into your bundle like this:
byte[] bytes = myBuffer.getBytes( Charset.forName("UTF-8" ));
String byteString = new String( bytes, Charset.forName("UTF-8") );
myBundle.putString("param", byteString);
then reconstruct the bytebuffer from the string:
byte[] byteArray = byteString.getBytes();
ByteBuffer byteBuffer = ByteBuffer.allocate(byteArray.length + 8);
byteBuffer.put(byteArray);

Related

How can we create our own data type in java?

I have a requirement where I have to create my own data type and i should assign some bytes to the data types.
Example:
Datatype A : should have 1 byte of memory
Datatype B : should have 2 bytes of memory
Datatype C : should have 7 bytes of memeory etc..
Is there any way we can define our own data types and allocate some memory to them ?
There are a few "options" in Java to express types, namely: interfaces, classes, and enums.
In that sense, the best match would be a class, like:
public class TwoByteHolder {
public final byte[] data = new byte[2];
}
An object of that class allows you to exactly store 2 bytes; the next "level" could be something like:
public class ByteHolder {
public final byte[] data;
public ByteHolder(int numberOfBytes) {
data = new byte[ numberOfBytes ];
}
...
But of course: the memory overhead would be enormous - Java isn't the the best language to deal with such requirements.
The only user-defined types available in Java are classes (including enums), and you cannot directly control how large they are. A class instance has many bytes of overhead you can't avoid having.
You can create a new class which has specific fields. If you need exact size of fields you can use byte arrays.
class Data {
public byte[] dataA = new byte[1];
public byte[] dataB = new byte[2];
public byte[] dataC = new byte[7];
...
}
You can't create data types in java, but you can create object classes, like this:
Class A {
public byte[] bytes;
}

Google Protobuf ByteString vs. Byte[]

I am working with google protobuf in Java.
I see that it is possible to serialize a protobuf message to String, byte[], ByteString, etc:
(Source: https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/MessageLite)
I don't know what a ByteString is. I got the following definition from the the protobuf API documentation (source: https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/ByteString):
"Immutable sequence of bytes. Substring is supported by sharing the reference to the immutable underlying bytes, as with String."
It is not clear to me how a ByteString is different from a String or byte[].
Can somebody please explain?
Thanks.
You can think of ByteString as an immutable byte array. That's pretty much it. It's a byte[] which you can use in a protobuf. Protobuf does not let you use Java arrays because they're mutable.
ByteString exists because String is not suitable for representing arbitrary sequences of bytes. String is specifically for character data.
The protobuf MessageLite Interface provides toByteArray() and toByteString() methods. If ByteString is an immutable byte[], would the byte representation of a message represented by both ByteString and byte[] be the same?
Sort of. If you call toByteArray() you'll get the same value as if you were to call toByteString().toByteArray(). Compare the implementation of the two methods, in AbstractMessageLite:
public ByteString toByteString() {
try {
final ByteString.CodedBuilder out =
ByteString.newCodedBuilder(getSerializedSize());
writeTo(out.getCodedOutput());
return out.build();
} catch (IOException e) {
throw new RuntimeException(
"Serializing to a ByteString threw an IOException (should " +
"never happen).", e);
}
}
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
final CodedOutputStream output = CodedOutputStream.newInstance(result);
writeTo(output);
output.checkNoSpaceLeft();
return result;
} catch (IOException e) {
throw new RuntimeException(
"Serializing to a byte array threw an IOException " +
"(should never happen).", e);
}
}
A ByteString gives you the ability to perform more operations on the underlying data without having to copy the data into a new structure. For instance, if you wanted to provide a subset of bytes in a byte[] to another method, you would need to supply it with a start index and an end index. You can also concatenate ByteStrings without having to create a new data structure and manually copy the data.
However, with a ByteString you can give the method a subset of that data without the method knowing anything about the underlying storage. Just like a a substring of a normal String.
A String is for representing text and is not a good way to store binary data (as not all binary data has a textual equivalent unless you encode it in a manner that does: e.g. hex or Base64).

ByteBuffer.allocate within Constructors

In my code I have a class with a ByteBuffer and two Constructors. Depending on the constructor, I want to allocate a different about of space to the ByteBuffer.
ByteBuffer data = ByteBuffer.allocate(1);
1st_constructor(arg1, arg2, arg3){
data = ByteBuffer.allocate(5);
}
1st_constructor(arg1, arg2){
data = ByteBuffer.allocate(10);
}
I was wondering, is this the correct way to do this? I only declared the ByteBuffer outside the constructors because I thought this is the only way the instantiated object would be able to access it (not sure if that is right though?)
Thank you for your help.
This is the correct way:
final ByteBuffer data;
1st_constructor(arg1, arg2, arg3){
data = ByteBuffer.allocate(5);
}
1st_constructor(arg1, arg2){
data = ByteBuffer.allocate(10);
}
Not sure why you have
ByteBuffer data = ByteBuffer.allocate(1);
Either mark it as final as above, or move it into a separate default constructor if that's your intention.

Could ByteBuffer implement DataOutput/DataInput?

Is there some subtle reason why java.nio.ByteBuffer does not implement java.io.DataOutput or java.io.DataInput, or did the authors just not choose to do this? It would seem straightforward to map the calls (e.g. putInt() -> writeInt()).
The basic problem I (and some others, apparently) have is older classes which know how to serialize/serialize themselves using the generic interfaces: DataInput/DataOutput. I would like to reuse my custom serialization without writing a custom proxy for ByteBuffer.
Just wrap the buffer in ByteArrayInputStream or ByteArrayOutputStream using the put() or wrap() methods. The problem with having a ByteBuffer directly emulate a datainput/output stream has to do with not knowing the sizes in advance. What if there's an overrun?
What is needed is a ByteBufferOutputStream in which you can wrap / expose the required behaviors. Examples of this exist; the Apache avro serialization scheme has such a thing. It's not too hard to roll your own. Why is there not one by default? Well, it's not a perfect world...
ByteArrayOutputStream backing = new ByteArrayOutputStream();
DataOutput foo = new DataOutputStream(backing);
// do your serialization out to foo
foo.close();
ByteBuffer buffer = ByteBuffer.wrap(backing.toByteArray());
// now you've got a bytebuffer...
A better way that works with direct buffers too:
class ByteBufferOutputStream extends OutputStream
{
private final ByteBuffer buffer;
public ByteBufferOutputStream(ByteBuffer buffer)
{
this.buffer = buffer;
}
public void write(int b) throws IOException
{
buffer.put((byte) b);
}
}
Note that this requires calling buffer.flip() after you are done writing to it, before you can read from it.

How to convert a non serializable object to byte array?

I'm trying to use javax.crypto.Cipher.doFinal(byte[]) method to encrypt an object. But, for security reasons, the object cannot be serializable.
So, how to convert the object to byte array without serialization?
--update
is using serialization the only way to use this Cipher method? Because as I know important data should not be serializable.
I used com.fasterxml.jackson.databind.ObjectMapper.
private static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.writeValue(os, obj);
return os.toByteArray();
}
You just serialize each of it's components. Recurse. Eventually you end up with native objects that you can serialize.
If you implement this by implementing java's serialization methods, java will ensure that you do not serialize any object twice and will take care of references for you.
In short, make the object serializable.
Solved,
instead of use a getByteArray() to call Cipher.doFinal(), I'll use Cipher.doFinal() inside the class, with a getEncryptedByteArray() method; so I serialize the data inside the class without making the class itself serializable, and the return result will be encrypted.
Any objection to this approach will be considered.. :)
Here is a simple example of serializing a class to a byte array.
public Class Foo {
private boolean isHappy;
private short happyCount;
private Bar bar;
public byte[] serializeData () throws IOException
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream( stream );
out.writeBoolean(isHappy);
out.writeShort( slope );
// Serialize bar which will just append to this byte stream
bar.doSerializeData(out);
// Return the serialized object.
byte[] data = stream.toByteArray();
// Clean up.
stream.close();
return data;
}
}
Of course, a lot of the details in your case depend on your class structure but hopefully this gets you pointed in the right direction.
To deserialize you just need to reverse the above.
java.beans.XMLEncoder/Decoder.

Categories