org.zeromq.proto.ZPicture Maven / Gradle / Ivy
Show all versions of jeromq Show documentation
package org.zeromq.proto;
import java.util.Locale;
import java.util.regex.Pattern;
import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZMQException;
import org.zeromq.ZMsg;
import zmq.ZError;
import zmq.util.Draft;
/**
* De/serialization of data within a message.
*
* This is a DRAFT class, and may change without notice.
*/
@Draft
public class ZPicture
{
private static final Pattern FORMAT = Pattern.compile("[i1248sbcfz]*m?");
private static final Pattern BINARY_FORMAT = Pattern.compile("[1248sSbcf]*m?");
/**
* Creates a binary encoded 'picture' message to the socket (or actor), so it can be sent.
* The arguments are encoded in a binary format that is compatible with zproto, and
* is designed to reduce memory allocations.
*
* @param picture The picture argument is a string that defines the
* type of each argument. Supports these argument types:
*
*
* Types of arguments
* pattern java type zproto type
* 1 int type = "number" size = "1"
* 2 int type = "number" size = "2"
* 4 long type = "number" size = "3"
* 8 long type = "number" size = "4"
* s String, 0-255 chars type = "string"
* S String, 0-2^32-1 chars type = "longstr"
* b byte[], 0-2^32-1 bytes type = "chunk"
* c byte[], 0-2^32-1 bytes type = "chunk"
* f ZFrame type = "frame"
* m ZMsg type = "msg" Has to be the last element of the picture
*
* @param args Arguments according to the picture
* @return a new {@link ZMsg} that encode the arguments
* @api.note Does not change or take ownership of any arguments.
*/
@Draft
public ZMsg msgBinaryPicture(String picture, Object... args)
{
if (!BINARY_FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected binary format " + BINARY_FORMAT.pattern(),
ZError.EPROTO);
}
ZMsg msg = new ZMsg();
// Pass 1: calculate total size of data frame
int frameSize = 0;
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case '1': {
frameSize += 1;
break;
}
case '2': {
frameSize += 2;
break;
}
case '4': {
frameSize += 4;
break;
}
case '8': {
frameSize += 8;
break;
}
case 's': {
String string = (String) args[index];
frameSize += 1 + (string != null ? string.getBytes(ZMQ.CHARSET).length : 0);
break;
}
case 'S': {
String string = (String) args[index];
frameSize += 4 + (string != null ? string.getBytes(ZMQ.CHARSET).length : 0);
break;
}
case 'b':
case 'c': {
byte[] block = (byte[]) args[index];
frameSize += 4 + block.length;
break;
}
case 'f': {
ZFrame frame = (ZFrame) args[index];
msg.add(frame);
break;
}
case 'm': {
ZMsg other = (ZMsg) args[index];
if (other == null) {
msg.add(new ZFrame((byte[]) null));
}
else {
msg.addAll(other);
}
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
// Pass 2: encode data into data frame
ZFrame frame = new ZFrame(new byte[frameSize]);
ZNeedle needle = new ZNeedle(frame);
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case '1': {
needle.putNumber1((int) args[index]);
break;
}
case '2': {
needle.putNumber2((int) args[index]);
break;
}
case '4': {
needle.putNumber4((int) args[index]);
break;
}
case '8': {
needle.putNumber8((long) args[index]);
break;
}
case 's': {
needle.putString((String) args[index]);
break;
}
case 'S': {
needle.putLongString((String) args[index]);
break;
}
case 'b':
case 'c': {
byte[] block = (byte[]) args[index];
needle.putNumber4(block.length);
needle.putBlock(block, block.length);
break;
}
case 'f':
case 'm':
break;
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
msg.addFirst(frame);
return msg;
}
@Draft
public boolean sendBinaryPicture(Socket socket, String picture, Object... args)
{
return msgBinaryPicture(picture, args).send(socket);
}
/**
* Receive a binary encoded 'picture' message from the socket (or actor).
* This method is similar to {@link org.zeromq.ZMQ.Socket#recv()}, except the arguments are encoded
* in a binary format that is compatible with zproto, and is designed to
* reduce memory allocations.
*
* @param picture The picture argument is a string that defines
* the type of each argument. See {@link #sendBinaryPicture(Socket, String, Object...)}
* for the supported argument types.
* @return the picture elements as object array
**/
@Draft
public Object[] recvBinaryPicture(Socket socket, final String picture)
{
if (!BINARY_FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected binary format " + BINARY_FORMAT.pattern(),
ZError.EPROTO);
}
ZFrame frame = ZFrame.recvFrame(socket);
if (frame == null) {
return null;
}
// Get the data frame
ZNeedle needle = new ZNeedle(frame);
Object[] results = new Object[picture.length()];
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case '1': {
results[index] = needle.getNumber1();
break;
}
case '2': {
results[index] = needle.getNumber2();
break;
}
case '4': {
results[index] = needle.getNumber4();
break;
}
case '8': {
results[index] = needle.getNumber8();
break;
}
case 's': {
results[index] = needle.getString();
break;
}
case 'S': {
results[index] = needle.getLongString();
break;
}
case 'b':
case 'c': {
int size = needle.getNumber4();
results[index] = needle.getBlock(size);
break;
}
case 'f': {
// Get next frame off socket
results[index] = ZFrame.recvFrame(socket);
break;
}
case 'm': {
// Get zero or more remaining frames
results[index] = ZMsg.recvMsg(socket);
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
return results;
}
/**
* Queues a 'picture' message to the socket (or actor), so it can be sent.
*
* @param picture The picture is a string that defines the type of each frame.
* This makes it easy to send a complex multiframe message in
* one call. The picture can contain any of these characters,
* each corresponding to zero or one arguments:
*
*
*
* Types of arguments
* i = int (stores signed integer)
* 1 = byte (stores 8-bit unsigned integer)
* 2 = int (stores 16-bit unsigned integer)
* 4 = long (stores 32-bit unsigned integer)
* 8 = long (stores 64-bit unsigned integer)
* s = String
* b = byte[]
* c = byte[]
* f = ZFrame
* m = ZMsg (sends all frames in the ZMsg)Has to be the last element of the picture
* z = sends zero-sized frame (0 arguments)
*
* Note that s, b, f and m are encoded the same way and the choice is
* offered as a convenience to the sender, which may or may not already
* have data in a ZFrame or ZMsg. Does not change or take ownership of
* any arguments.
*
* Also see {@link #recvPicture(Socket, String)}} how to recv a
* multiframe picture.
* @param args Arguments according to the picture
* @return true if successful, false if sending failed for any reason
*/
@Draft
public boolean sendPicture(Socket socket, String picture, Object... args)
{
if (!FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected format " + FORMAT.pattern(), ZError.EPROTO);
}
ZMsg msg = new ZMsg();
for (int pictureIndex = 0, argIndex = 0; pictureIndex < picture.length(); pictureIndex++, argIndex++) {
char pattern = picture.charAt(pictureIndex);
switch (pattern) {
case 'i': {
msg.add(String.format(Locale.ENGLISH, "%d", (int) args[argIndex]));
break;
}
case '1': {
msg.add(String.format(Locale.ENGLISH, "%d", (0xff) & (int) args[argIndex]));
break;
}
case '2': {
msg.add(String.format(Locale.ENGLISH, "%d", (0xffff) & (int) args[argIndex]));
break;
}
case '4': {
msg.add(String.format(Locale.ENGLISH, "%d", (0xffffffff) & (int) args[argIndex]));
break;
}
case '8': {
msg.add(String.format(Locale.ENGLISH, "%d", (long) args[argIndex]));
break;
}
case 's': {
msg.add((String) args[argIndex]);
break;
}
case 'b':
case 'c': {
msg.add((byte[]) args[argIndex]);
break;
}
case 'f': {
msg.add((ZFrame) args[argIndex]);
break;
}
case 'm': {
ZMsg msgParm = (ZMsg) args[argIndex];
while (!msgParm.isEmpty()) {
msg.add(msgParm.pop());
}
break;
}
case 'z': {
msg.add((byte[]) null);
argIndex--;
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
return msg.send(socket, false);
}
/**
* Receive a 'picture' message to the socket (or actor).
*
*
* @param picture The picture is a string that defines the type of each frame.
* This makes it easy to recv a complex multiframe message in
* one call. The picture can contain any of these characters,
* each corresponding to zero or one elements in the result:
*
*
*
* Types of arguments
* i = int (stores signed integer)
* 1 = int (stores 8-bit unsigned integer)
* 2 = int (stores 16-bit unsigned integer)
* 4 = long (stores 32-bit unsigned integer)
* 8 = long (stores 64-bit unsigned integer)
* s = String
* b = byte[]
* f = ZFrame (creates zframe)
* m = ZMsg (creates a zmsg with the remaing frames)
* z = null, asserts empty frame (0 arguments)
*
*
* Also see {@link #sendPicture(Socket, String, Object...)} how to send a
* multiframe picture.
*
* @return the picture elements as object array
*/
@Draft
public Object[] recvPicture(Socket socket, String picture)
{
if (!FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected format " + FORMAT.pattern(), ZError.EPROTO);
}
Object[] elements = new Object[picture.length()];
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case 'i': {
elements[index] = Integer.valueOf(socket.recvStr());
break;
}
case '1': {
elements[index] = (0xff) & Integer.parseInt(socket.recvStr());
break;
}
case '2': {
elements[index] = (0xffff) & Integer.parseInt(socket.recvStr());
break;
}
case '4': {
elements[index] = (0xffffffff) & Integer.parseInt(socket.recvStr());
break;
}
case '8': {
elements[index] = Long.valueOf(socket.recvStr());
break;
}
case 's': {
elements[index] = socket.recvStr();
break;
}
case 'b':
case 'c': {
elements[index] = socket.recv();
break;
}
case 'f': {
elements[index] = ZFrame.recvFrame(socket);
break;
}
case 'm': {
elements[index] = ZMsg.recvMsg(socket);
break;
}
case 'z': {
ZFrame zeroFrame = ZFrame.recvFrame(socket);
if (zeroFrame == null || zeroFrame.size() > 0) {
throw new ZMQException("zero frame is not empty", ZError.EPROTO);
}
elements[index] = new ZFrame((byte[]) null);
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
return elements;
}
}