Coverage Report - org.jscsi.target.scsi.ScsiResponseDataSegment
 
Classes in this File Line Coverage Branch Coverage Complexity
ScsiResponseDataSegment
0%
0/42
0%
0/28
3.375
 
 1  
 package org.jscsi.target.scsi;
 2  
 
 3  
 
 4  
 import java.nio.ByteBuffer;
 5  
 
 6  
 import org.jscsi.target.connection.TargetPduFactory;
 7  
 import org.jscsi.target.scsi.sense.SenseData;
 8  
 import org.jscsi.target.util.Debug;
 9  
 import org.jscsi.target.util.ReadWrite;
 10  
 import org.slf4j.Logger;
 11  
 import org.slf4j.LoggerFactory;
 12  
 
 13  
 
 14  
 /**
 15  
  * Instances of this class represent data that may be sent in a SCSI Command Response PDU.
 16  
  * <p>
 17  
  * It may contain either {@link SenseData}, {@link IResponseData}, or (rarely) both. Some SCSI commands require that no
 18  
  * response data is sent (i.e. data segment length = 0) - in these cases the {@link #EMPTY_DATA_SEGMENT} shall be used.
 19  
  * <p>
 20  
  * Since the target must honor the buffer size the initiator has allocated for data returned in the data segment, the
 21  
  * {@link #serialize()} method will only write as many bytes to the passed {@link ByteBuffer} as specified during
 22  
  * initialization.
 23  
  * 
 24  
  * @see TargetPduFactory
 25  
  * @author Andreas Ergenzinger
 26  
  */
 27  
 public final class ScsiResponseDataSegment {
 28  
 
 29  0
     private static final Logger LOGGER = LoggerFactory.getLogger(ScsiResponseDataSegment.class);
 30  
 
 31  
     /**
 32  
      * A {@link ScsiResponseDataSegment} of length zero.
 33  
      */
 34  0
     public static final ScsiResponseDataSegment EMPTY_DATA_SEGMENT = new ScsiResponseDataSegment();
 35  
 
 36  
     /**
 37  
      * The length in bytes of the SENSE LENGTH field.
 38  
      */
 39  
     private static final int SENSE_LENGTH_FIELD_LENGTH = 2;
 40  
 
 41  
     /**
 42  
      * The contained {@link SenseData}.
 43  
      */
 44  
     private final SenseData senseData;
 45  
 
 46  
     /**
 47  
      * The contained {@link IResponseData}.
 48  
      */
 49  
     private final IResponseData responseData;
 50  
 
 51  
     /**
 52  
      * The number of bytes the initiator has allocated for data sent in the data segment, i.e. the maximum number of
 53  
      * bytes of this object that may be transmitted.
 54  
      */
 55  
     private final int allocationLength;
 56  
 
 57  
     /**
 58  
      * The length of the {@link ScsiResponseDataSegment}, without considering any limitations due to
 59  
      * {@link #allocationLength}, or <code>-1</code>.
 60  
      * 
 61  
      * @see #uncroppedSize()
 62  
      */
 63  0
     private int uncroppedSize = -1;
 64  
 
 65  
     /**
 66  
      * Constructor for creating an empty {@link ScsiResponseDataSegment}.
 67  
      * 
 68  
      * @see ScsiResponseDataSegment#EMPTY_DATA_SEGMENT
 69  
      */
 70  
     private ScsiResponseDataSegment () {
 71  0
         this(null, null, 0);
 72  0
     }
 73  
 
 74  
     /**
 75  
      * Creates a {@link ScsiResponseDataSegment} with {@link SenseData}.
 76  
      * 
 77  
      * @param senseData the sense data sent as the result of SCSI error
 78  
      * @param allocationLength number of bytes the initiator has allocated for data sent in the data segment
 79  
      */
 80  
     public ScsiResponseDataSegment (final SenseData senseData, final int allocationLength) {
 81  0
         this(senseData, null, allocationLength);
 82  0
     }
 83  
 
 84  
     /**
 85  
      * Creates a {@link ScsiResponseDataSegment} with {@link IResponseData}.
 86  
      * 
 87  
      * @param responseData the data requested by the initiator
 88  
      * @param allocationLength number of bytes the initiator has allocated for data sent in the data segment
 89  
      */
 90  
     public ScsiResponseDataSegment (final IResponseData responseData, final int allocationLength) {
 91  0
         this(null, responseData, allocationLength);
 92  0
     }
 93  
 
 94  
     /**
 95  
      * Creates a {@link ScsiResponseDataSegment} with both {@link SenseData} and {@link IResponseData}.
 96  
      * 
 97  
      * @param senseData senseData the sense data sent as the result of SCSI error
 98  
      * @param responseData data requested by the initiator
 99  
      * @param allocationLength number of bytes the initiator has allocated for data sent in the data segment
 100  
      */
 101  0
     private ScsiResponseDataSegment (final SenseData senseData, final IResponseData responseData, final int allocationLength) {
 102  0
         this.senseData = senseData;
 103  0
         this.responseData = responseData;
 104  0
         this.allocationLength = allocationLength;
 105  0
     }
 106  
 
 107  
     /**
 108  
      * Returns a {@link ByteBuffer} containing a serialized representation of this object.
 109  
      * 
 110  
      * @return a {@link ByteBuffer} containing a serialized representation of this object
 111  
      */
 112  
     public ByteBuffer serialize () {
 113  
 
 114  0
         final int size = uncroppedSize();
 115  0
         if (size == 0) return ByteBuffer.allocate(0);// empty data segment
 116  
 
 117  
         // calculate sense length
 118  
         int senseLength;
 119  0
         if (senseData == null)
 120  0
             senseLength = 0;
 121  
         else
 122  0
             senseLength = senseData.size();
 123  
 
 124  
         // allocate buffer of appropriate size
 125  0
         final ByteBuffer buffer = ByteBuffer.allocate(size);
 126  
 
 127  
         // write sense length field
 128  0
         ReadWrite.writeTwoByteInt(buffer,// buffer
 129  
                 senseLength,// value
 130  
                 0);// index
 131  
 
 132  
         // write sense data
 133  0
         if (senseData != null) senseData.serialize(buffer, SENSE_LENGTH_FIELD_LENGTH);
 134  
 
 135  
         // write and SCSI response data
 136  0
         if (responseData != null) responseData.serialize(buffer, SENSE_LENGTH_FIELD_LENGTH + senseLength);
 137  
 
 138  0
         if (allocationLength > 0 && buffer.capacity() > allocationLength) {
 139  
             /*
 140  
              * If the allocation length variable is valid and the response data segment is larger than the data-in
 141  
              * buffer allocated by the initiator, limit the number of bytes returned.
 142  
              */
 143  0
             buffer.position(0);
 144  0
             buffer.limit(allocationLength);
 145  0
             final ByteBuffer croppedBuffer = ByteBuffer.allocate(allocationLength);
 146  0
             croppedBuffer.put(buffer);
 147  0
             return croppedBuffer;
 148  
         }
 149  
 
 150  0
         if (LOGGER.isDebugEnabled()) LOGGER.debug("SCSI Response Data Segment:\n" + Debug.byteBufferToString(buffer));
 151  
 
 152  0
         return buffer;
 153  
     }
 154  
 
 155  
     /**
 156  
      * The length of the {@link ScsiResponseDataSegment}, without considering any limitations due to
 157  
      * {@link #allocationLength}.
 158  
      * <p>
 159  
      * The returned value is calculated just once, and then stored in {@link #uncroppedSize}. This summation is
 160  
      * performed only if {@link #uncroppedSize} is negative.
 161  
      * 
 162  
      * @return length of the {@link ScsiResponseDataSegment}, without considering any limitations due to
 163  
      *         {@link #allocationLength}
 164  
      */
 165  
     public int uncroppedSize () {
 166  0
         if (uncroppedSize < 0) {
 167  0
             if (senseData == null && responseData == null) return 0;
 168  0
             int size = SENSE_LENGTH_FIELD_LENGTH;
 169  0
             if (senseData != null) size += senseData.size();
 170  0
             if (responseData != null) size += responseData.size();
 171  0
             uncroppedSize = size;
 172  
         }
 173  0
         return uncroppedSize;
 174  
     }
 175  
 
 176  
     /**
 177  
      * Indicates if any bytes have to be cropped during {@link #serialize()} calls. The method returns <code>true</code>
 178  
      * if and only if the serialized length of this objects exceeds {@link #allocationLength}.
 179  
      * 
 180  
      * @return <code>true</code> if and only if the serialized length of this objects exceeds {@link #allocationLength}
 181  
      */
 182  
     public boolean getResidualOverflow () {
 183  0
         if (uncroppedSize() > allocationLength) return true;
 184  0
         return false;
 185  
     }
 186  
 
 187  
     /**
 188  
      * Returns the number of bytes that had to be cropped due to the total length of all contained fields exceeding the
 189  
      * {@link #allocationLength}.
 190  
      * 
 191  
      * @return the total number of bytes that have to be cropped during serialization
 192  
      * @see #serialize()
 193  
      */
 194  
     public int getResidualCount () {
 195  0
         if (getResidualOverflow()) return uncroppedSize() - allocationLength;
 196  0
         return 0;
 197  
     }
 198  
 }