/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.hdfs;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import org.apache.hadoop.hdfs.profiling.DFSWriteProfilingData.WritePacketClientProfile;
import org.apache.hadoop.hdfs.protocol.DataTransferProtocol;
import org.apache.hadoop.hdfs.server.datanode.DataNode;
/****************************************************************
* Packet for DFSOutputStream to use to construct the packet to send to data
* node. Checksums are inlined.
****************************************************************/
class DFSOutputStreamPacketInlineChecksum extends DFSOutputStreamPacket {
ByteBuffer buffer; // only one of buf and buffer is non-null
byte[] buf;
int dataStart;
int pos;
static final long HEART_BEAT_SEQNO = -1L;
/**
* create a heartbeat packet
*/
DFSOutputStreamPacketInlineChecksum(DFSOutputStream dfsOutputStream) {
super(dfsOutputStream);
buffer = null;
int packetSize = this.dfsOutputStream.getPacketHeaderLen()
+ DFSClient.SIZE_OF_INTEGER;
buf = new byte[packetSize];
pos = dataStart;
}
// create a new packet
DFSOutputStreamPacketInlineChecksum(DFSOutputStream dfsOutputStream,
int pktSize, int chunksPerPkt, long offsetInBlock,
WritePacketClientProfile profile) throws IOException {
super(dfsOutputStream, chunksPerPkt, offsetInBlock, profile);
buffer = null;
buf = new byte[pktSize];
dataStart = dfsOutputStream.getPacketHeaderLen()
+ DFSClient.SIZE_OF_INTEGER;
pos = dataStart;
}
@Override
void writeData(byte[] inarray, int off, int len) throws IOException {
if (len > this.dfsOutputStream.getBytesPerChecksum()) {
throw new IOException("Chunk size is larger than limit.");
}
if (pos + len > buf.length) {
throw new BufferOverflowException();
}
System.arraycopy(inarray, off, buf, pos, len);
dataLength += len;
pos += len;
}
@Override
void writeChecksum(byte[] inarray, int off, int len) throws IOException {
if (len != this.dfsOutputStream.getChecksumSize()) {
throw new IOException("Checksum size doesn't match.");
}
if (pos + len > buf.length) {
throw new BufferOverflowException();
}
System.arraycopy(inarray, off, buf, pos, len);
pos += len;
}
/**
* Returns ByteBuffer that contains one full packet, including header.
*
* Packet format:
*
* +----------------------------+
* | size of payload |
* +----------------------------+
* | packetVersion* |
* +----------------------------+
* | offset of data in block |
* +----------------------------+
* | packet's seq ID |
* +----------------------------+
* | |
* | payload** |
* | |
* +----------------------------+
*
* * Only for data protocol version not ealier than
* PACKET_INCLUDE_VERSION_VERSION
* ** payload format is per packet version and will be described below.
*
* If packet size = 0, it indicates the end of the stream and there will be
* no packetVersion or data after it sent.
*
* == payload format ==
*
* +----------------------------+
* | data size to be sent |
* +----------------------------+
* | |
* | data chunk 1 |
* | (can be partial) |
* | |
* +----------------------------+
* | checksum for chunk 1 |
* +----------------------------+
* | |
* | data chunk 2 |
* | |
* +----------------------------+
* | checksum for chunk 2 |
* +----------------------------+
* | |
* | ... |
* | |
* +----------------------------+
* | |
* | last data chunk |
* | (can be partial) |
* +----------------------------+
* | checksum for last chunk |
* +----------------------------+
*
* The way data and checksums are inlined is the same as inline checksum
* on disk files.
*
* @throws IOException
*/
ByteBuffer getBuffer() throws IOException {
/*
* Once this is called, no more data can be added to the packet. setting
* 'buf' to null ensures that. This is called only when the packet is ready
* to be sent.
*/
if (buffer != null) {
return buffer;
}
int pktLen = DFSClient.SIZE_OF_INTEGER + (pos - dataStart);
// normally dataStart == checksumPos, i.e., offset is zero.
buffer = ByteBuffer.wrap(buf, 0, dfsOutputStream.getPacketHeaderLen()
+ pktLen);
buf = null;
buffer.mark();
/*
* write the header and data length. The format is described in comment
* before DataNode.BlockSender
*/
buffer.putInt(pktLen); // pktSize
buffer.putInt(dfsOutputStream.getPacketVersion());
buffer.putLong(offsetInBlock);
buffer.putLong(seqno);
byte booleanFieldValue = 0x00;
if (lastPacketInBlock) {
booleanFieldValue |= DataNode.isLastPacketInBlockMask;
}
if (dfsOutputStream.ifForceSync()) {
booleanFieldValue |= DataNode.forceSyncMask;
}
buffer.put(booleanFieldValue);
// end of pkt header
buffer.putInt(dataLength); // actual data length, excluding checksum.
buffer.reset();
return buffer;
}
long getEndPosInCurrBlk() {
return offsetInBlock + dataLength;
}
/**
* Check if this packet is a heart beat packet
*
* @return true if the sequence number is HEART_BEAT_SEQNO
*/
boolean isHeartbeatPacket() {
return seqno == HEART_BEAT_SEQNO;
}
@Override
void cleanup() {
pos = dataStart;
dataLength = 0;
buffer = null;
int packetSize = this.dfsOutputStream.getPacketHeaderLen()
+ DFSClient.SIZE_OF_INTEGER;
buf = new byte[packetSize];
}
}