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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class OptionPacket {
private static final Logger LOGGER = LoggerFactory.getLogger(OptionPacket.class);
/** Magic */
public static final long MAGIC = 0x49484156454F5054L;
/** Static Size */
private static final int HEADER_SIZE = 64 / 8 + 32 / 8 + 32 / 8;
/** Magic number */
private final long magicNumber;
/** Option code */
private final OptionCmd optionCode;
/** Size of the data */
private final long size;
public OptionPacket(final long magicNumber, final OptionCmd optionCode, final long size) {
this.magicNumber = magicNumber;
this.optionCode = optionCode;
this.size = size;
}
/**
* Gets the magic number.
*
* @return the magic number
*/
public final long getMagicNumber() {
return magicNumber;
}
/**
* Gets the option code.
*
* @return the option code
*/
public final OptionCmd getOptionCode() {
return optionCode;
}
/**
* Gets the size of the next data.
*
* @return the data size
*/
public final long getSize() {
return size;
}
/**
* Allocate buffer for a {@link OptionPacket}.
*
* @return the allocated {@link ByteBuffer}
*/
public static final ByteBuffer allocateHeader() {
return (ByteBuffer) NbdByteBufferCache.allocate(Utils.MAX_HEADER_SIZE).limit(HEADER_SIZE);
}
/**
* Release an array of {@link ByteBuffer}.
*
* @param buffers
*/
public static final void release(final ByteBuffer[] buffers) {
for (final ByteBuffer buffer : buffers) {
NbdByteBufferCache.release(buffer);
}
}
/**
* Release a {@link ByteBuffer}.
*
* @param buffer
*/
public static final void release(final ByteBuffer buffer) {
NbdByteBufferCache.release(buffer);
}
/**
* Serialize a {@link OptionPacket} in a {@link ByteBuffer}.
*
* @param packet
* the packet to serialize
* @param data
* the data to add
* @return the {@link ByteBuffer}
*/
public static final ByteBuffer[] serialize(final OptionPacket packet, final String data) {
final ByteBuffer header = allocateHeader();
Utils.putUnsignedLong(header, packet.magicNumber);
Utils.putUnsignedInt(header, packet.optionCode.value());
Utils.putUnsignedInt(header, packet.size);
header.flip();
if (packet.size != 0) {
final ByteBuffer body = allocateData(Utils.getUnsignedIntPositive(packet.size));
if (data != null) {
body.put(data.getBytes());
}
body.flip();
final ByteBuffer[] buffers = { header, body };
return buffers;
}
else {
final ByteBuffer[] buffers = { header };
return buffers;
}
}
/**
* Decode and construct an {@link OptionPacket} instance from a {@link ByteBuffer}.
*
* @param buffer
* the {@link ByteBuffer} to decode
*
* @return the {@link OptionPacket}
*
* @throws NbdException
* if something is malformed in the received packet
*/
public static final OptionPacket deserialize(final ByteBuffer buffer) throws NbdException {
// No need to check the sign
final long magicNumber = Utils.getUnsignedLong(buffer);
if (magicNumber != MAGIC) {
throw new NbdException("Illegal magic number for option: " + Long.toHexString(magicNumber));
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("magicNumber=0x" + Long.toHexString(magicNumber));
}
final OptionCmd optionCode = OptionCmd.valueOf(Utils.getUnsignedInt(buffer));
final long size = Utils.getUnsignedInt(buffer);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("size=0x" + Long.toHexString(size));
}
return new OptionPacket(magicNumber, optionCode, size);
}
/**
* 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);
}
/**
* Decode a data {@link ByteBuffer} in a String
*
* @param dst
* the {@link ByteBuffer} to decode
*
* @return a String
*/
public static final String getData(final ByteBuffer dst) {
return new String(dst.array());
}
}