001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.net.protocol;
005:
006: import com.tc.bytes.TCByteBuffer;
007: import com.tc.bytes.TCByteBufferFactory;
008: import com.tc.util.Assert;
009:
010: import java.util.zip.Adler32;
011:
012: /**
013: * TODO: document me
014: *
015: * @author teck
016: */
017: public abstract class AbstractTCNetworkHeader implements
018: TCNetworkHeader {
019: protected static final int LENGTH_NOT_AVAIL = -1;
020:
021: private static final byte[] EMTPY_BYTE_ARRAY = new byte[] {};
022: private static final byte[] FOUR_ZERO_BYTES = new byte[] {
023: (byte) 0, (byte) 0, (byte) 0, (byte) 0 };
024:
025: protected final int minLength;
026: protected final int maxLength;
027: protected TCByteBuffer data;
028: private final boolean localAllocation;
029:
030: abstract protected void setHeaderLength(short headerLength);
031:
032: /**
033: * Override this method to allow protocol parsers to wait before asking <code>getHeaderByteLength()</code>. This is
034: * really only useful if your header length data is not the first byte of your header
035: *
036: * @return true if the length of this header is available
037: */
038: protected boolean isHeaderLengthAvail() {
039: return true;
040: }
041:
042: protected AbstractTCNetworkHeader(TCByteBuffer buffer, int min,
043: int max) {
044: this .minLength = min;
045: this .maxLength = max;
046:
047: if (buffer == null) {
048: this .data = TCByteBufferFactory.getInstance(false, max);
049: this .data.limit(min);
050: localAllocation = true;
051: } else {
052: this .data = buffer;
053: localAllocation = false;
054: }
055:
056: Assert.eval(!this .data.isDirect());
057: Assert.eval(this .data.capacity() >= this .maxLength);
058: if (this .data.limit() % 4 != 0) {
059: throw new AssertionError(
060: "buffer limit not a multiple of 4: "
061: + this .data.limit());
062: }
063: }
064:
065: protected AbstractTCNetworkHeader(int min, int max) {
066: this (null, min, max);
067: }
068:
069: public TCByteBuffer getDataBuffer() {
070: return data;
071: }
072:
073: abstract public void validate() throws TCProtocolException;
074:
075: public void recycle() {
076: Assert.assertTrue(localAllocation);
077: if (data != null) {
078: data.recycle();
079: data = null;
080: } else {
081: // data is already null. Probably called recycle twice !!
082: Thread.dumpStack();
083: }
084: }
085:
086: private void setBytes(int pos, byte[] value) {
087: setBytes(pos, value, 0, value.length);
088: }
089:
090: private void setBytes(int pos, byte[] value, int offset, int length) {
091: data.put(pos, value, offset, length);
092: }
093:
094: protected byte getByte(int index) {
095: return data.get(index);
096: }
097:
098: protected byte[] getBytes(int offset, int len) {
099: Assert.eval(len >= 0);
100:
101: if (0 == len) {
102: return EMTPY_BYTE_ARRAY;
103: }
104:
105: byte rv[] = new byte[len];
106: data.get(offset, rv, 0, len);
107:
108: return rv;
109: }
110:
111: protected void set4BitValue(int pos, boolean high, byte value) {
112: byte other4 = (byte) (data.get(pos) & (high ? 0x0F : 0xF0));
113: byte val = (byte) ((value << (high ? 4 : 0)) & (high ? 0xF0
114: : 0x0F));
115:
116: data.put(pos, (byte) (val | other4));
117: }
118:
119: protected byte get4BitValue(int pos, boolean high) {
120: byte bite = getByte(pos);
121:
122: if (high) {
123: return (byte) ((bite >> 4) & 0x0F);
124: } else {
125: return (byte) (bite & 0x0F);
126: }
127: }
128:
129: protected long computeAdler32Checksum(int pos, boolean set) {
130: Adler32 adler = new Adler32();
131:
132: // save off the existing checksum
133: byte cksum[] = getBytes(pos, 4);
134:
135: // zero out the checksum bytes before doing the calculation
136: setBytes(pos, FOUR_ZERO_BYTES);
137: adler.update(data.array(), 0, getHeaderByteLength());
138:
139: long rv = adler.getValue();
140:
141: if (set) {
142: data.putUint(pos, rv);
143: } else {
144: // restore the original checksum bytes
145: setBytes(pos, cksum);
146: }
147:
148: return rv;
149: }
150:
151: protected void setLimit(int newLimit) {
152: data.limit(newLimit);
153: }
154:
155: /**
156: * @param options zero-padded header option bytes. The byte array can be zero length and/or null to indicate that no
157: * options should be set in this header
158: */
159: public void setOptions(byte[] options) {
160: if (options == null) {
161: options = new byte[] {};
162: }
163:
164: int optionsLen = options.length;
165:
166: Assert.eval((optionsLen % 4) == 0);
167: Assert.eval(optionsLen <= (maxLength - minLength));
168:
169: if (optionsLen > 0) {
170: setLimit(minLength + optionsLen);
171: setBytes(minLength, options);
172: } else {
173: setLimit(minLength);
174: }
175:
176: setHeaderLength((byte) ((minLength + optionsLen) / 4));
177: }
178:
179: public byte[] getOptions() {
180: return getBytes(minLength, getHeaderByteLength() - minLength);
181: }
182:
183: }
|