/*
* Copyright (c) 2012-2015 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.netty4.handler.codec.zmtp;
import java.nio.ByteBuffer;
import io.netty.buffer.ByteBuf;
class ZMTP10WireFormat implements ZMTPWireFormat {
private static final byte FINAL_FLAG = 0x0;
private static final byte MORE_FLAG = 0x1;
/**
* Read the remote identity octets from a ZMTP/1.0 greeting.
*/
static ByteBuffer readIdentity(final ByteBuf buffer) throws ZMTPParsingException {
final long length = readLength(buffer);
if (length == -1) {
return null;
}
final long identityLength = length - 1;
if (identityLength < 0 || identityLength > 255) {
throw new ZMTPParsingException("Bad remote identity length: " + length);
}
// skip the flags byte
buffer.skipBytes(1);
final byte[] identity = new byte[(int) identityLength];
buffer.readBytes(identity);
return ByteBuffer.wrap(identity);
}
/**
* Read a ZMTP/1.0 frame length.
*/
static long readLength(final ByteBuf in) {
if (in.readableBytes() < 1) {
return -1;
}
long size = in.readByte() & 0xFF;
if (size == 0xFF) {
if (in.readableBytes() < 8) {
return -1;
}
size = in.readLong();
}
return size;
}
/**
* Write a ZMTP/1.0 frame length.
*
* @param length The length.
* @param out Target buffer.
*/
static void writeLength(final long length, final ByteBuf out) {
writeLength(out, length, length);
}
/**
* Write a ZMTP/1.0 frame length.
*
* @param out Target buffer.
* @param maxLength The maximum length of the field.
* @param length The length.
*/
static void writeLength(final ByteBuf out, final long maxLength, final long length) {
if (maxLength < 255) {
out.writeByte((byte) length);
} else {
out.writeByte(0xFF);
out.writeLong(length);
}
}
/**
* Write a ZMTP/1.0 greeting.
*
* @param out Target buffer.
* @param identity Socket identity.
*/
static void writeGreeting(final ByteBuf out, final ByteBuffer identity) {
writeLength(identity.remaining() + 1, out);
out.writeByte(0x00);
out.writeBytes(identity.duplicate());
}
@Override
public Header header() {
return new ZMTP10Header();
}
@Override
public int frameLength(final int content) {
if (content + 1 < 255) {
return 1 + 1 + content;
} else {
return 1 + 8 + 1 + content;
}
}
static class ZMTP10Header implements Header {
int maxLength;
int length;
boolean more;
@Override
public void set(final int maxLength, final int length, final boolean more) {
this.maxLength = maxLength;
this.length = length;
this.more = more;
}
@Override
public void write(final ByteBuf out) {
writeLength(out, maxLength + 1, length + 1);
out.writeByte(more ? MORE_FLAG : FINAL_FLAG);
}
@Override
public boolean read(final ByteBuf in) throws ZMTPParsingException {
final long len = readLength(in);
if (len == -1) {
// Wait for more data
return false;
}
if (len == 0) {
throw new ZMTPParsingException("Received frame with zero length");
}
if (in.readableBytes() < 1) {
// Wait for more data
return false;
}
length = (int) len - 1;
more = (in.readByte() & MORE_FLAG) == MORE_FLAG;
return true;
}
@Override
public long length() {
return length;
}
@Override
public boolean more() {
return more;
}
}
}