package org.nd4j.aeron.ipc.chunk;
import lombok.Builder;
import lombok.Data;
import org.nd4j.aeron.ipc.AeronNDArraySubscriber;
import org.nd4j.aeron.ipc.NDArrayMessage;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* An NDArrayMessageChunk
* represents a chunked {@link NDArrayMessage}
* that needs to be reassembled.
*
* This chunking is for large messages
* that need to be segmented to be sent over the wire.
*
* An {@link AeronNDArraySubscriber}
* would use this information to assemble a contiguous buffer
* to be used for assembling a large {@link NDArrayMessage}
*
* Of note is that this chunk will also contain the needed data
* for assembling in addition to the desired metadata such as the chunk size.
*
* A chunk has an idea which is used to track the chunk
* across fragmentations, an index fr determining ordering
* for re assembling, and metadata about the chunk such as
* the chunk size and number of chunks
*
*
*
* @author Adam Gibson
*/
@Data
@Builder
public class NDArrayMessageChunk implements Serializable {
//id of the chunk (meant for tracking for reassembling)
private String id;
//the chunk size (message size over the network)
private int chunkSize;
//the message type, this should be chunked
private NDArrayMessage.MessageType messageType;
//the number of chunks for reassembling the message
private int numChunks;
//the index of this particular chunk
private int chunkIndex;
//the actual chunk data
private ByteBuffer data;
/**
* Returns the overall size for an {@link NDArrayMessageChunk}.
* The size of a message chunk is:
* idLengthSize(4) + messageTypeSize(4) + indexSize(4) + chunkSizeSize(4) + numChunksSize(4) + chunk.getData().limit() + chunk.getId().getBytes().length
* Many of these are flat out integers and are mainly variables for accounting purposes and ease of readbility
* @param chunk the size of a message chunk
* @return the size of an {@link ByteBuffer} for the given {@link NDArrayMessageChunk}
*/
public static int sizeForMessage(NDArrayMessageChunk chunk) {
int messageTypeSize = 4;
int indexSize = 4;
int numChunksSize = 4;
int chunkSizeSize = 4;
int idLengthSize = 4;
return idLengthSize + messageTypeSize + indexSize + chunkSizeSize + numChunksSize + chunk.getData().limit()
+ chunk.getId().getBytes().length;
}
/**
* Convert an ndarray message chunk to a buffer.
* @param chunk the chunk to convert
* @return an {@link ByteBuffer} based on the
* passed in message chunk.
*/
public static ByteBuffer toBuffer(NDArrayMessageChunk chunk) {
ByteBuffer ret = ByteBuffer.allocateDirect(sizeForMessage(chunk)).order(ByteOrder.nativeOrder());
//the messages type enum as an int
ret.putInt(chunk.getMessageType().ordinal());
//the number of chunks this chunk is apart of
ret.putInt(chunk.getNumChunks());
//the chunk size
ret.putInt(chunk.getChunkSize());
//the length of the id (for self describing purposes)
ret.putInt(chunk.getId().getBytes().length);
// the actual id as a string
ret.put(chunk.getId().getBytes());
//the chunk index
ret.putInt(chunk.getChunkIndex());
//the actual data
ret.put(chunk.getData());
return ret;
}
/**
* Returns a chunk given the passed in {@link ByteBuffer}
* NOTE THAT THIS WILL MODIFY THE PASSED IN BYTEBUFFER's POSITION.
*
* @param byteBuffer the byte buffer to extract the chunk from
* @return the ndarray message chunk based on the passed in {@link ByteBuffer}
*/
public static NDArrayMessageChunk fromBuffer(ByteBuffer byteBuffer, NDArrayMessage.MessageType type) {
int numChunks = byteBuffer.getInt();
int chunkSize = byteBuffer.getInt();
int idLength = byteBuffer.getInt();
byte[] id = new byte[idLength];
byteBuffer.get(id);
String idString = new String(id);
int index = byteBuffer.getInt();
ByteBuffer firstData = byteBuffer.slice();
NDArrayMessageChunk chunk = NDArrayMessageChunk.builder().chunkSize(chunkSize).numChunks(numChunks)
.data(firstData).messageType(type).id(idString).chunkIndex(index).build();
return chunk;
}
}