package io.eguan.nbdsrv.packet;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* 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.
* #L%
*/
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class OptionReplyPacket {
private static final Logger LOGGER = LoggerFactory.getLogger(OptionReplyPacket.class);
private static final ByteBuffer[] EMPTY_BUFFER_ARRAY = new ByteBuffer[0];
private static final String[] EMPTY_DATA_ARRAY = new String[0];
/** Magic */
public static final long MAGIC = 0x3e889045565a9L;
/** Static size */
private static final int HEADER_SIZE = 64 / 8 + 32 / 8 + 32 / 8 + 32 / 8;
/** Magic Number */
private final long magic;
/** Option code */
private final OptionCmd optionCmd;
/** Option reply code */
private final OptionReplyCmd replyCmd;
/** The data size */
private long dataSize;
public OptionReplyPacket(final long magic, final OptionCmd option, final OptionReplyCmd reply) {
super();
this.magic = magic;
this.optionCmd = option;
this.replyCmd = reply;
}
/**
* Get the magic number.
*
* @return the magic number
*/
public final long getMagic() {
return magic;
}
/**
* Gets the command in the reply.
*
* @return the {@link OptionCmd}
*/
public final OptionCmd getOptionCmd() {
return optionCmd;
}
/**
* Gets the reply.
*
* @return the {@link OptionReplyCmd}
*/
public final OptionReplyCmd getReplyCmd() {
return replyCmd;
}
/**
* Gets the data size.
*
* @return the size of the data
*/
public final long getDataSize() {
return dataSize;
}
/**
* Sets the data size.
*
* @param dataSize
* the size of the data
*/
public final void setDataSize(final long dataSize) {
this.dataSize = dataSize;
}
/**
* Allocate {@link ByteBuffer} for {@link OptionReplyPacket}.
*
* @return the allocated {@link ByteBuffer}
*/
public static final ByteBuffer allocateHeader() {
return (ByteBuffer) NbdByteBufferCache.allocate(Utils.MAX_HEADER_SIZE).limit(HEADER_SIZE);
}
/**
* Release a {@link ByteBuffer}.
*
* @param buffer
* the buffer to release
*/
public static final void release(final ByteBuffer buffer) {
NbdByteBufferCache.release(buffer);
}
/**
* Release an array of {@link ByteBuffer}
*
* @param buffers
* the array of buffer to release
*/
public static final void release(final ByteBuffer[] buffers) {
for (final ByteBuffer buffer : buffers) {
NbdByteBufferCache.release(buffer);
}
}
/**
* Serialize a {@link OptionReplyPacket} in a {@link ByteBuffer}
*
* @param packet
* the {@link OptionReplyPacket} to serialize
* @param data
* the data to transfer - May not be null
*
* @return the {@link ByteBuffer}
*/
public static final ByteBuffer serialize(final OptionReplyPacket packet, final String data) {
final ByteBuffer buffer;
if (data.length() != 0) {
buffer = NbdByteBufferCache.allocate(HEADER_SIZE + data.length() + 4);
}
else {
buffer = allocateHeader();
}
Utils.putUnsignedLong(buffer, packet.magic);
Utils.putUnsignedInt(buffer, packet.optionCmd.value());
Utils.putUnsignedInt(buffer, packet.replyCmd.value());
if (data.length() != 0) {
Utils.putUnsignedInt(buffer, data.length() + 4);
Utils.putUnsignedInt(buffer, data.length());
buffer.put(data.getBytes());
}
else {
Utils.putUnsignedInt(buffer, 0);
}
buffer.flip();
return buffer;
}
/**
* Serialize a {@link OptionReplyPacket} in an array {@link ByteBuffer}
*
* @param packet
* the {@link OptionReplyPacket} to serialize
* @param data
* the array of data to transfer- May be null
*
* @return the {@link ByteBuffer}
*/
public static final ByteBuffer[] serializeMultiple(final OptionReplyPacket packet, final String[] data) {
final List<ByteBuffer> buffersList = new ArrayList<>();
if (data != null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("data.length= " + data.length);
}
for (int i = 0; i < data.length; i++) {
final ByteBuffer buffer = serialize(packet, data[i]);
buffersList.add(buffer);
}
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("No data");
}
final ByteBuffer buffer = serialize(packet, "");
buffersList.add(buffer);
}
return buffersList.toArray(EMPTY_BUFFER_ARRAY);
}
/**
* Deserialize a {@link ByteBuffer} in {@link OptionReplyPacket}.
*
* @param buffer
* the {@link ByteBuffer} to deserialize
*
* @return the {@link OptionReplyPacket}
*/
public static final OptionReplyPacket deserialize(final ByteBuffer buffer) {
final long magic = Utils.getUnsignedLong(buffer);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("magic=0x" + Long.toHexString(magic));
}
final long value = Utils.getUnsignedInt(buffer);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("option=0x" + Long.toHexString(value));
}
final OptionCmd option = OptionCmd.valueOf(value);
final long value2 = Utils.getUnsignedInt(buffer);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("reply=0x" + Long.toHexString(value2));
}
final OptionReplyCmd reply = OptionReplyCmd.valueOf(value2);
final long size = Utils.getUnsignedInt(buffer);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("size=0x" + Long.toHexString(size));
}
final OptionReplyPacket packet = new OptionReplyPacket(magic, option, reply);
packet.dataSize = size;
return packet;
}
/**
* Gets data contains in the option reply packet
*
* @param buffer
* the buffer to decode
* @param cmd
* the cmd reply
* @return an array of String contained in the ByteBuffer
*/
public static final String[] getData(final ByteBuffer buffer, final OptionReplyPacket packet) {
final List<String> dataList = new ArrayList<>();
if (packet.replyCmd.equals(OptionReplyCmd.NBD_REP_SERVER)) {
long dataSize = packet.dataSize;
while (dataSize != 0) {
final long size = Utils.getUnsignedInt(buffer);
final byte[] b = new byte[Utils.getUnsignedIntPositive(size)];
buffer.get(b);
final String s = new String(b);
dataList.add(s);
// Data size + an integer for the size
dataSize -= size + 4;
}
}
return dataList.toArray(EMPTY_DATA_ARRAY);
}
/**
* Allocate a {@link ByteBuffer} to receive data.
*
* @param size
* the size of the buffer
*
* @return the allocated {@link ByteBuffer}
*/
public static final ByteBuffer allocateData(final int size) {
return NbdByteBufferCache.allocate(size);
}
}