com.spotify.netty.handler.codec.zmtp.ZMTPUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.spotify.netty.handler.codec.zmtp.ZMTPUtils.java

Source

/*
 * Copyright (c) 2012-2013 Spotify AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.spotify.netty.handler.codec.zmtp;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;

import java.util.List;
import java.util.UUID;

import static java.nio.ByteOrder.BIG_ENDIAN;

/**
 * Helper utilities for zmtp protocol
 */
public class ZMTPUtils {

    public static final byte MORE_FLAG = 0x1;
    public static final byte FINAL_FLAG = 0x0;
    public static final ZMTPFrame DELIMITER = ZMTPFrame.EMPTY_FRAME;

    /**
     * Helper to decode a ZMTP/1.0 length field
     *
     * @return length
     * @throws IndexOutOfBoundsException if there is not enough octets to be read.
     */
    static public long decodeLength(final ByteBuf in) {
        long size = in.readByte() & 0xFF;
        if (size == 0xFF) {
            if (in.readableBytes() < 8) {
                return -1;
            }
            if (in.order() == BIG_ENDIAN) {
                size = in.readLong();
            } else {
                size = ByteBufUtil.swapLong(in.readLong());
            }
        }

        return size;
    }

    static public void encodeLength(final long size, final ByteBuf out) {
        encodeLength(size, out, false);
    }

    /**
     * Helper to encode a zmtp length field
     */
    static public void encodeLength(final long size, final ByteBuf out, boolean forceLong) {
        if (size < 255 && !forceLong) {

            // Encoded as a single byte
            out.writeByte((byte) size);
        } else {
            out.writeByte(0xFF);
            writeLong(out, size);
        }
    }

    static void encodeZMTP2FrameHeader(final long size, final byte flags, final ByteBuf out) {
        if (size < 256) {
            out.writeByte(flags);
            out.writeByte((byte) size);
        } else {
            out.writeByte(flags | 0x02);
            writeLong(out, size);
        }
    }

    static void writeLong(final ByteBuf buffer, final long value) {
        if (buffer.order() == BIG_ENDIAN) {
            buffer.writeLong(value);
        } else {
            buffer.writeLong(ByteBufUtil.swapLong(value));
        }
    }

    /**
     * Returns a byte array from a UUID
     *
     * @return byte array format (big endian)
     */
    static public byte[] getBytesFromUUID(final UUID uuid) {
        final long most = uuid.getMostSignificantBits();
        final long least = uuid.getLeastSignificantBits();

        return new byte[] { (byte) (most >>> 56), (byte) (most >>> 48), (byte) (most >>> 40), (byte) (most >>> 32),
                (byte) (most >>> 24), (byte) (most >>> 16), (byte) (most >>> 8), (byte) most, (byte) (least >>> 56),
                (byte) (least >>> 48), (byte) (least >>> 40), (byte) (least >>> 32), (byte) (least >>> 24),
                (byte) (least >>> 16), (byte) (least >>> 8), (byte) least };
    }

    /**
     * Writes a ZMTP frame to a buffer.
     *
     * @param frame  The frame to write.
     * @param buffer The target buffer.
     * @param more   True to write a more flag, false to write a final flag.
     */
    public static void writeFrame(final ZMTPFrame frame, final ByteBuf buffer, final boolean more,
            final int version) {
        if (version == 1) {
            encodeLength(frame.size() + 1, buffer);
            buffer.writeByte(more ? MORE_FLAG : FINAL_FLAG);
        } else { // version == 2
            encodeZMTP2FrameHeader(frame.size(), more ? MORE_FLAG : FINAL_FLAG, buffer);
        }
        if (frame.hasData()) {
            //      final ByteBuf source = frame.getDataBuffer();
            byte[] data = frame.getData();
            buffer.ensureWritable(data.length);

            buffer.writeBytes(data);
            //      source.getBytes(source.readerIndex(), buffer, source.readableBytes());
        }
    }

    /**
     * Write a ZMTP message to a buffer.
     *
     * @param message   The message to write.
     * @param buffer    The target buffer.
     * @param enveloped Whether the envelope and delimiter should be written.
     */
    @SuppressWarnings("ForLoopReplaceableByForEach")
    public static void writeMessage(final ZMTPMessage message, final ByteBuf buffer, final boolean enveloped,
            int version) {
        // Write envelope
        if (enveloped) {
            // Sanity check
            if (message.getContent().isEmpty()) {
                throw new IllegalArgumentException("Cannot write enveloped message with no content");
            }

            final List<ZMTPFrame> envelope = message.getEnvelope();
            for (int i = 0; i < envelope.size(); i++) {
                writeFrame(envelope.get(i), buffer, true, version);
            }

            // Write the delimiter
            writeFrame(DELIMITER, buffer, true, version);
        }

        final List<ZMTPFrame> content = message.getContent();
        final int n = content.size();
        final int lastFrame = n - 1;
        for (int i = 0; i < n; i++) {
            writeFrame(content.get(i), buffer, i < lastFrame, version);
        }
    }

    /**
     * Calculate bytes needed to serialize a ZMTP frame.
     *
     * @param frame The frame.
     * @return Bytes needed.
     */
    public static int frameSize(final ZMTPFrame frame, int version) {
        if (version == 1) {
            if (frame.size() + 1 < 255) {
                return 1 + 1 + frame.size();
            } else {
                return 1 + 8 + 1 + frame.size();
            }
        } else { // version 2
            if (frame.size() < 256) {
                return 1 + 1 + frame.size();
            } else {
                return 1 + 8 + frame.size();
            }
        }
    }

    /**
     * Calculate bytes needed to serialize a ZMTP message.
     *
     * @param message   The message.
     * @param enveloped Whether an envelope will be written.
     * @return The number of bytes needed.
     */
    @SuppressWarnings("ForLoopReplaceableByForEach")
    public static int messageSize(final ZMTPMessage message, final boolean enveloped, final int version) {
        final int contentSize = framesSize(message.getContent(), version);
        if (!enveloped) {
            return contentSize;
        }
        final int envelopeSize = framesSize(message.getEnvelope(), version) + frameSize(DELIMITER, version);
        return envelopeSize + contentSize;
    }

    /**
     * Calculate bytes needed to serialize a list of ZMTP frames.
     */
    @SuppressWarnings("ForLoopReplaceableByForEach")
    public static int framesSize(final List<ZMTPFrame> frames, final int version) {
        int size = 0;
        final int n = frames.size();
        for (int i = 0; i < n; i++) {
            size += frameSize(frames.get(i), version);
        }
        return size;
    }

    /**
     * Create a string from binary data, keeping printable ascii and hex encoding everything else.
     *
     * @param data The data
     * @return A string representation of the data
     */
    public static String toString(final byte[] data) {
        if (data == null) {
            return null;
        }
        return toString(Unpooled.wrappedBuffer(data));
    }

    /**
     * Create a string from binary data, keeping printable ascii and hex encoding everything else.
     *
     * @param data The data
     * @return A string representation of the data
     */
    public static String toString(final ByteBuf data) {
        if (data == null) {
            return null;
        }
        final StringBuilder sb = new StringBuilder();
        for (int i = data.readerIndex(); i < data.writerIndex(); i++) {
            final byte b = data.getByte(i);
            if (b > 31 && b < 127) {
                if (b == '%') {
                    sb.append('%');
                }
                sb.append((char) b);
            } else {
                sb.append('%');
                sb.append(String.format("%02X", b));
            }
        }
        return sb.toString();
    }

    public static String toString(final List<ZMTPFrame> frames) {
        final StringBuilder builder = new StringBuilder("[");
        for (int i = 0; i < frames.size(); i++) {
            final ZMTPFrame frame = frames.get(i);
            builder.append('"');
            builder.append(frame.getData());
            builder.append('"');
            if (i < frames.size() - 1) {
                builder.append(',');
            }
        }
        builder.append(']');
        return builder.toString();
    }
}