I've just completed a major cleanup/reorganization of:

org.apache.tomcat.modules.server.Ajp13

In the jakarta-tomcat branch, which I'm attaching as a patch against the current 
version.  I have done MINIMAL testing, since I am very new to working on Tomcat, and I 
don't know what a standard set of tests might be (or if one exists).  I have verified 
that Tomcat can still talk to Apache, and that basic request info is being transfered. 
 That's about all I've been able to do.

The main goal of the rewrite was to clarify what is going on.  To that end, I added a 
lot of comments, and moved some code around to clarify what it was doing.  I also: 

 - Fixed a minor bug in headerNameToSc -- response headers of type "WWW-Authenticate" 
were not being encoded as integers, because of a typo in the Java code.  Note that the 
old behavior was less efficient (it would send the entire "WWW-Authenticate" string), 
but not incorrect.

 - Cleaned up all the import statements
 
 - Stripped some unused code out of the internal Ajp13Packet class.

 - Moved a lot of functionality out of tomcat.util.BuffTool into Ajp13Packet (where it 
seemed much more natural).  I believe that BuffTool can now be safely deleted (I just 
deleted it and built Tomcat with no problems).
 
Enjoy,
-Dan

-- 
Dan Milstein // [EMAIL PROTECTED]
Index: Ajp13.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-tomcat/src/share/org/apache/tomcat/modules/server/Ajp13.java,v
retrieving revision 1.5
diff -u -r1.5 Ajp13.java
--- Ajp13.java  2000/11/30 04:58:45     1.5
+++ Ajp13.java  2000/12/05 04:25:53
@@ -59,27 +59,51 @@
 
 package org.apache.tomcat.modules.server;
 
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import org.apache.tomcat.core.*;
-import org.apache.tomcat.util.*;
-
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Enumeration;
+
+import org.apache.tomcat.core.Request;
+import org.apache.tomcat.util.MimeHeaders;
+import org.apache.tomcat.util.MessageBytes;
+
+/**
+ * Represents a single, persistent connection between the web server and
+ * the servlet container.  Uses the Apache JServ Protocol version 1.3 for
+ * communication.  Because this protocal does not multiplex requests, this
+ * connection can only be associated with a single request-handling cycle
+ * at a time.<P>
+ *
+ * This class contains knowledge about how an individual packet is laid out
+ * (via the internal <CODE>Ajp13Packet</CODE> class), and also about the
+ * stages of communicaton between the server and the servlet container.  It
+ * translates from Tomcat's internal servlet support methods
+ * (e.g. doWrite) to the correct packets to send to the web server.
+ *
+ * @see Ajp13Interceptor 
+ */
 public class Ajp13
 {
     public static final int MAX_PACKET_SIZE=8192;
-    public static final int H_SIZE=4;
+    public static final int H_SIZE=4;  // Size of basic packet header
 
     public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
     public static final int  MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE - 4;
 
+    // Prefix codes for message types from server to container
     public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
     public static final byte JK_AJP13_SHUTDOWN          = 7;
        
+    // Prefix codes for message types from container to server
     public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
     public static final byte JK_AJP13_SEND_HEADERS      = 4;
     public static final byte JK_AJP13_END_RESPONSE      = 5;
+    public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
     
+    // Integer codes for common response header strings
     public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
     public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
     public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
@@ -91,11 +115,10 @@
     public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
     public static final int SC_RESP_STATUS              = 0xA00A;
     public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
-    
-    public static final byte JK_AJP13_GET_BODY_CHUNK = 6;
        
-    public static final byte SC_A_CONTEXT       = 1;
-    public static final byte SC_A_SERVLET_PATH  = 2;
+    // Integer codes for common (optional) request attribute names
+    public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
+    public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
     public static final byte SC_A_REMOTE_USER   = 3;
     public static final byte SC_A_AUTH_TYPE     = 4;
     public static final byte SC_A_QUERY_STRING  = 5;
@@ -103,9 +126,14 @@
     public static final byte SC_A_SSL_CERT      = 7;
     public static final byte SC_A_SSL_CIPHER    = 8;
     public static final byte SC_A_SSL_SESSION   = 9;
-    public static final byte SC_A_REQ_ATTRIBUTE = 10;
+
+    // Used for attributes which are not in the list above
+    public static final byte SC_A_REQ_ATTRIBUTE = 10; 
+
+    // Terminates list of attributes
     public static final byte SC_A_ARE_DONE      = (byte)0xFF;
 
+    // Translates integer codes to names of HTTP methods
     public static final String []methodTransArray = {
         "OPTIONS",
         "GET",
@@ -116,6 +144,7 @@
         "TRACE"
     };
     
+    // Translates integer codes to request header names
     public static final String []headerTransArray = {
         "accept",
         "accept-charset",
@@ -133,86 +162,118 @@
         "user-agent"
     };
 
+
+    // ============ Instance Properties ====================
+
     OutputStream out;
     InputStream in;
        
-    Ajp13Packet outBuf=new Ajp13Packet( MAX_PACKET_SIZE );;
-    Ajp13Packet inBuf=new Ajp13Packet( MAX_PACKET_SIZE );;
+    Ajp13Packet outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
+    Ajp13Packet inBuf  = new Ajp13Packet( MAX_PACKET_SIZE );
 
+    // Holds incoming reads of request body data (*not* header data)
     byte []bodyBuff = new byte[MAX_READ_SIZE];
-    int blen;
-    int pos;
+    
+    int blen;  // Length of current chunk of body data in buffer
+    int pos;   // Current read position within that buffer
 
     public Ajp13() 
     {
         super();
     }
 
+    public void recycle() 
+    {
+    }
+    
+    /**
+     * Associate an open socket with this instance.
+     */
     public void setSocket( Socket socket ) throws IOException {
        socket.setSoLinger( true, 100);
        out = socket.getOutputStream();
-       in = socket.getInputStream();
-       pos=0;
+       in  = socket.getInputStream();
+       pos = 0;
     }
 
+    /**
+     * Read a new packet from the web server and decode it.  If it's a
+     * forwarded request, store its properties in the passed-in Request
+     * object.
+     *
+     * @param req An empty (newly-recycled) request object.
+     * 
+     * @return 200 in case of a successful read of a forwarded request, 500
+     * if there were errors in the reading of the request, and -2 if the
+     * server is asking the container to shut itself down.  
+     */
     public int receiveNextRequest(Request req) throws IOException 
     {
+       // XXX The return values are awful.
+
        int err = receive(inBuf);
        if(err < 0) {
            return 500;
        }
        
-       // XXX right now the only incoming packet is "new request"
-       // We need to deal with arbitrary calls
        int type = (int)inBuf.getByte();
        switch(type) {
            
        case JK_AJP13_FORWARD_REQUEST:
            return decodeRequest(req, inBuf);
-
+           
        case JK_AJP13_SHUTDOWN:
            return -2;
        }
-       return 200;
+       return 200; // XXX This is actually an error condition 
     }
 
+    /**
+     * Parse a FORWARD_REQUEST packet from the web server and store its
+     * properties in the passed-in request object.
+     *
+     * @param req An empty (newly-recycled) request object.
+     * @param msg Holds the packet which has just been sent by the web
+     * server, with its read position just past the packet header (which in
+     * this case includes the prefix code for FORWARD_REQUEST).
+     *
+     * @return 200 in case of a successful decoduing, 500 in case of error.  
+     */
     private int decodeRequest( Request req, Ajp13Packet msg ) throws IOException
     {
-       
+       // XXX Awful return values
+
         boolean isSSL = false;
-        byte bsc;
-        int  hCount = 0;
+
+        // Translate the HTTP method code to a String.
+        byte methodCode = msg.getByte();
+        req.method().setString( methodTransArray[(int)methodCode - 1] );
 
-        /*
-         * Read the method and translate it to a String
-         */
-        bsc        = msg.getByte();
-        req.method().setString( methodTransArray[(int)bsc - 1] );
-        req.setProtocol( msg.getString());
-        req.requestURI().setString(  msg.getString());
+        req.setProtocol(            msg.getString());
+        req.requestURI().setString( msg.getString());
 
-        req.setRemoteAddr( msg.getString());
-        req.setRemoteHost( msg.getString());
+        req.setRemoteAddr(          msg.getString());
+        req.setRemoteHost(          msg.getString());
         req.serverName().setString( msg.getString());
-        req.setServerPort( msg.getInt());
+        req.setServerPort(          msg.getInt());
 
-       bsc        = msg.getByte();
-        if(bsc != 0) {
-            isSSL = true;
-        }
+       isSSL = (msg.getByte() != 0);
 
        // Decode headers
-       MimeHeaders headers=req.getMimeHeaders();
-       hCount     = msg.getInt();
+       MimeHeaders headers = req.getMimeHeaders();
+       int hCount = msg.getInt();
         for(int i = 0 ; i < hCount ; i++) {
             String hName = null;
 
+           // Header names are encoded as either an integer code starting
+           // with 0xA0, or as a normal string (in which case the first
+           // two bytes are the length).
             int isc = msg.peekInt();
-            int hId = isc & 0x000000FF;
+            int hId = isc & 0xFF;
 
-            isc &= 0x0000FF00;
-            if(0x0000A000 == isc) {
-                msg.getInt();
+            isc &= 0xFF00;
+            if(0xA000 == isc) {
+                msg.getInt(); // To advance the read position
                 hName = headerTransArray[hId - 1];
             } else {
                 hName = msg.getString().toLowerCase();
@@ -222,10 +283,11 @@
             headers.addValue( hName ).setString( hValue );
         }
 
-        for(bsc = msg.getByte() ;
-            bsc != SC_A_ARE_DONE ;
-            bsc = msg.getByte()) {
-            switch(bsc) {
+       byte attributeCode;
+        for(attributeCode = msg.getByte() ;
+            attributeCode != SC_A_ARE_DONE ;
+            attributeCode = msg.getByte()) {
+            switch(attributeCode) {
            case SC_A_CONTEXT      :
                //              contextPath = msg.getString();
                 break;
@@ -270,11 +332,12 @@
                
            case SC_A_REQ_ATTRIBUTE :
                isSSL = true;
-               req.setAttribute(msg.getString(), msg.getString());
+               req.setAttribute(msg.getString(), 
+                                msg.getString());
                 break;
 
            default:
-               return 500;
+               return 500; // Error
             }
         }
 
@@ -282,6 +345,8 @@
             req.scheme().setString("https");
         }
 
+       // Check to see if there should be a body packet coming along
+       // immediately after
        MessageBytes clB=headers.getValue("content-length");
         int contentLength = (clB==null) ? -1 : clB.getInt();
        if(contentLength > 0) {
@@ -296,20 +361,38 @@
            msg.getBytes(bodyBuff);
        }
     
-        return 200;
+        return 200; // Success
     }
+
+    // ==================== Servlet Input Support =================
     
+    /**
+     * Return the next byte of request body data (to a servlet).
+     *
+     * @see Ajp13Request#doRead
+     */
     public int doRead() throws IOException 
     {
         if(pos >= blen) {
-            refeelReadBuffer();
+            refillReadBuffer();
         }
         return bodyBuff[pos++];
     }
     
+    /**
+     * Store a chunk of request data into the passed-in byte buffer.
+     *
+     * @param b A buffer to fill with data from the request.
+     * @param off The offset in the buffer at which to start filling.
+     * @param len The number of bytes to copy into the buffer.
+     *
+     * @return The number of bytes actually copied into the buffer.
+     *
+     * @see Ajp13Request#doRead
+     */
     public int doRead(byte[] b, int off, int len) throws IOException 
     {
-        // XXXXXX Stupid, but the whole thing must be rewriten ( see super()! )
+        // XXX Rewrite to use System.arrayCopy (please!)
         for(int i = off ; i < (len + off) ; i++) {
             int a = doRead();
             if(-1 == a) {
@@ -320,71 +403,71 @@
         
         return len;
     }
-    
-    public void recycle() 
-    {
-    }
     
-    public void refeelReadBuffer() throws IOException 
+    /**
+     * Get more request body data from the web server and store it in the 
+     * internal buffer.
+     */
+    private void refillReadBuffer() throws IOException 
     {
+       // Why not use outBuf??
        inBuf.reset();
-       Ajp13Packet msg = inBuf;
-       msg.appendByte(JK_AJP13_GET_BODY_CHUNK);
-       msg.appendInt(MAX_READ_SIZE);
-       send(msg);
+       inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
+       inBuf.appendInt(MAX_READ_SIZE);
+       send(inBuf);
        
-       int err = receive(msg);
+       int err = receive(inBuf);
         if(err < 0) {
            throw new IOException();
        }
        
-       blen = msg.peekInt();
+       blen = inBuf.peekInt();
        pos = 0;
-       msg.getBytes(bodyBuff);
+       inBuf.getBytes(bodyBuff);
     }    
 
-    // ==================== Output ====================
+    // ==================== Servlet Output Support =================
     
-    // XXX if more headers that MAX_SIZE, send 2 packets!   
+    /**
+     * Send the HTTP headers back to the web server and on to the browser.
+     *
+     * @param status The HTTP status code to send.
+     * @param headers The set of all headers.
+     */
     public void sendHeaders(int status, MimeHeaders headers) throws IOException 
     {
+       // XXX if more headers that MAX_SIZE, send 2 packets!   
        outBuf.reset();
-        Ajp13Packet msg=outBuf;
-        msg.reset();
-
-        msg.appendByte(JK_AJP13_SEND_HEADERS);
-        msg.appendInt(status);
-        msg.appendString("");
+        outBuf.appendByte(JK_AJP13_SEND_HEADERS);
+        outBuf.appendInt(status);
+        outBuf.appendString(""); // Http Status Message -- broken.
         
-        msg.appendInt(headers.size());
+        outBuf.appendInt(headers.size());
         
         Enumeration e = headers.names();
         while(e.hasMoreElements()) {
             String headerName = (String)e.nextElement();            
             int sc = headerNameToSc(headerName);
             if(-1 != sc) {
-                msg.appendInt(sc);
+                outBuf.appendInt(sc);
             } else {
-                msg.appendString(headerName);
+                outBuf.appendString(headerName);
             }
-            msg.appendString(headers.getHeader(headerName));
+            outBuf.appendString(headers.getHeader(headerName));
         }
 
-        msg.end();
-        send(msg);
+        outBuf.end();
+        send(outBuf);
     } 
-         
-    public void finish() throws IOException 
-    {
-       outBuf.reset();
-        Ajp13Packet msg = outBuf;
-        msg.reset();
-        msg.appendByte(JK_AJP13_END_RESPONSE);
-        msg.appendByte((byte)1);        
-        msg.end();
-        send(msg);
-    }
-    
+
+    /**
+     * Translate an HTTP response header name to an integer code if
+     * possible.  Case is ignored.
+     * 
+     * @param name The name of the response header to translate.
+     *
+     * @return The code for that header name, or -1 if no code exists.
+     */
     protected int headerNameToSc(String name)
     {       
         switch(name.charAt(0)) {
@@ -426,7 +509,7 @@
             
        case 'w':
        case 'W':
-           if(name.equalsIgnoreCase("WWW-Autheticate")) {
+           if(name.equalsIgnoreCase("WWW-Authenticate")) {
                return SC_RESP_WWW_AUTHENTICATE;
            }
             break;          
@@ -435,6 +518,27 @@
         return -1;
     }
 
+    /**
+     * Signal the web server that the servlet has finished handling this
+     * request.  
+     */
+    public void finish() throws IOException 
+    {
+       outBuf.reset();
+        outBuf.appendByte(JK_AJP13_END_RESPONSE);
+        outBuf.appendByte((byte)1);        
+        outBuf.end();
+        send(outBuf);
+    }
+
+    /**
+     * Send a chunk of response body data to the web server and on to the
+     * browser.
+     *
+     * @param b A huffer of bytes to send.
+     * @param off The offset into the buffer from which to start sending.
+     * @param len The number of bytes to send.
+     */    
     public void doWrite(  byte b[], int off, int len) throws IOException 
     {
        int sent = 0;
@@ -443,48 +547,73 @@
            to_send = to_send > MAX_SEND_SIZE ? MAX_SEND_SIZE : to_send;
 
            outBuf.reset();
-           Ajp13Packet buf = outBuf;
-           buf.reset();
-           buf.appendByte(JK_AJP13_SEND_BODY_CHUNK);                   
-           buf.appendBytes(b, off + sent, to_send);            
-           send(buf);
+           outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);                        
+           outBuf.appendBytes(b, off + sent, to_send);         
+           send(outBuf);
            sent += to_send;
        }
     }
     
 
-    public int receive(Ajp13Packet msg) throws IOException {
-       byte b[]=msg.getBuff();
+    // ========= Internal Packet-Handling Methods =================
+
+    /**
+     * Read in a packet from the web server and store it in the passed-in
+     * <CODE>Ajp13Packet</CODE> object.
+     *
+     * @param msg The object into which to store the incoming packet -- any
+     * current contents will be overwritten.
+     *
+     * @return The number of bytes read on a successful read or -1 if there 
+     * was an error.
+     **/
+    private int receive(Ajp13Packet msg) throws IOException {
+       // XXX If the length in the packet header doesn't agree with the
+       // actual number of bytes read, it should probably return an error
+       // value.  Also, callers of this method never use the length
+       // returned -- should probably return true/false instead.
+       byte b[] = msg.getBuff();
        
-       int rd=in.read( b, 0, H_SIZE );
-       if( rd<=0 ) {
-           //      System.out.println("Rd header returned: " + rd );
+       int rd = in.read( b, 0, H_SIZE );
+       if(rd <= 0) {
            return rd;
        }
        
-       int len=msg.checkIn();
+       int len = msg.checkIn();
        
        // XXX check if enough space - it's assert()-ed !!!
        // Can we have only one read ( with unblocking, it can read all at once - but 
maybe more ) ?
-       //???   len-=4; // header
        
-       rd=in.read( b, 4, len );
+       rd = in.read( b, 4, len );
        if( rd != len ) {
            System.out.println( "Incomplete read, deal with it " + len + " " + rd);
-           // ??? log
+           // XXX log
+           // XXX Return an error code?
        }
        //      msg.dump( "Incoming");
        return rd;
-       //    System.out.println( "Incoming Packet len=" + len);
     }
-       
-    public void send( Ajp13Packet msg ) throws IOException {
-       msg.end();
-       byte b[]=msg.getBuff();
-       int len=msg.getLen();
+
+    /**
+     * Send a packet to the web server.  Works for any type of message.
+     *
+     * @param msg A packet with accumulated data to send to the server --
+     * this method will write out the length in the header.  
+     */
+    private void send( Ajp13Packet msg ) throws IOException {
+       msg.end(); // Write the packet header
+       byte b[] = msg.getBuff();
+       int len  = msg.getLen();
        out.write( b, 0, len );
     }
        
+    /**
+     * Close the socket connection to the web server.  In general, sockets
+     * are maintained across many requests, so this will not be called
+     * after finish().  
+     *
+     * @see Ajp13Interceptor#processConnection
+     */
     public void close() throws IOException {
        if(null != out) {        
            out.close();
@@ -494,49 +623,53 @@
        }
     }
 
-    /** Encapsulated messages passed between Tomcat and Web servers
+    /**
+     * A single packet for communication between the web server and the
+     * container.  Designed to be reused many times with no creation of
+     * garbage.  Understands the format of data types for these packets.
+     * Can be used (somewhat confusingly) for both incoming and outgoing
+     * packets.  
      */
     public static class Ajp13Packet {
-       // previous name: MsgBuff
-       byte buff[];
-       int len;
-       int pos;
-       int maxlen;
-       
+       byte buff[]; // Holds the bytes of the packet
+       int pos;     // The current read or write position in the buffer
+
+       int len; 
+       // This actually means different things depending on whether the
+       // packet is read or write.  For read, it's the length of the
+       // payload (excluding the header).  For write, it's the length of
+       // the packet as a whole (counting the header).  Oh, well.
+
+       /**
+        * Create a new packet with an internal buffer of given size.
+        */
        public Ajp13Packet( int size ) {
-           buff=new byte[size];
-           maxlen=size;
+           buff = new byte[size];
        }
-       
-       public Ajp13Packet( byte b[] ) {
-           buff=b;
-           maxlen=b.length;
-       }
-       
+
        public byte[] getBuff() {
            return buff;
        }
        
-       public void setBuff(byte[] b) {
-           buff=b;
-           maxlen = b.length;
-       }
-       
        public int getLen() {
            return len;
        }
        
-       public int getMaxLen() {
-           return maxlen;
-       }
-       
-       /** Verify the buffer and read the len
+       /** 
+        * Parse the packet header for a packet sent from the web server to
+        * the container.  Set the read position to immediately after
+        * the header.
+        *
+        * @return The length of the packet payload, as encoded in the
+        * header, or -1 if the packet doesn't have a valid header.  
         */
        public int checkIn() {
-           pos=4;
-           int mark=BuffTool.getInt( buff,0 );
-           len=BuffTool.getInt( buff,2 );
+           pos = 0;
+           int mark = getInt();
+           len      = getInt();
+           
            if( mark != 0x1234 ) {
+               // XXX Logging
                System.out.println("BAD packet " + mark);
                dump( "In: " );
                return -1;
@@ -544,56 +677,122 @@
            return len;
        }
        
+       /**
+        * Prepare this packet for accumulating a message from the container to
+        * the web server.  Set the write position to just after the header
+        * (but leave the length unwritten, because it is as yet unknown).  
+        */
        public void reset() {
-           len=4;
-           pos=4;
-           buff[0]=(byte)'A';
-           buff[1]=(byte)'B';
+           len = 4;
+           pos = 4;
+           buff[0] = (byte)'A';
+           buff[1] = (byte)'B';
        }
        
+       /**
+        * For a packet to be sent to the web server, finish the process of
+        * accumulating data and write the length of the data payload into
+        * the header.  
+        */
        public void end() {
-           len=pos;
+           len = pos;
            setInt( 2, len-4 );
        }
        
-       public void setInt(int bpos, int val ) {
-           BuffTool.addInt( buff, bpos, val );
+       // ============ Data Writing Methods ===================
+
+       /**
+        * Write an integer at an arbitrary position in the packet, but don't
+        * change the write position.
+        *
+        * @param bpos The 0-indexed position within the buffer at which to
+        * write the integer (where 0 is the beginning of the header).
+        * @param val The integer to write.
+        */
+       private void setInt( int bPos, int val ) {
+           buff[bPos]   = (byte) ((val >>>  8) & 0xFF);
+           buff[bPos+1] = (byte) (val & 0xFF);
        }
-       
-       public void appendByte( byte val ) {
-           buff[pos] = val;
-           pos++;
+
+       public void appendInt( int val ) {
+           setInt( pos, val );
+           pos += 2;
        }
        
-       public void appendInt( int val ) {
-           BuffTool.addInt( buff, pos, val );
-           pos+=2;
+       public void appendByte( byte val ) {
+           buff[pos++] = val;
        }
        
-       public void appendString( String val ) {
-           pos=BuffTool.addString( buff, pos, val );
+       /**
+        * Write a String out at the current write position.  Strings are
+        * encoded with the length in two bytes first, then the string, and
+        * then a terminating \0 (which is <B>not</B> included in the
+        * encoded length).  The terminator is for the convenience of the C
+        * code, where it saves a round of copying.  A null string is
+        * encoded as a string with length 0.  
+        */
+       public void appendString( String str ) {
+           if(str != null) {
+               int strLen = str.length();
+               setInt( pos, strLen );
+               System.arraycopy( str.getBytes(), 0, buff, pos+2, strLen);
+               buff[pos + strLen + 2] = 0; // The \0 terminator
+               pos += strLen + 3;
+           }
+           else {
+               setInt( pos, 0);
+               buff[pos + 2] = 0;
+               pos += 3;
+           }     
+
        }
        
-       public void appendBytes( byte b[], int off, int len ) {
-           BuffTool.addInt( buff, pos, len );
-           pos+=2;
-           if( pos + len > buff.length ) {
-               System.out.println("Buffer overflow " + buff.length + " " + pos + " " 
+ len );
+       /** 
+        * Copy a chunk of bytes into the packet, starting at the current
+        * write position.  The chunk of bytes is encoded with the length
+        * in two bytes first, then the data itself, and finally a
+        * terminating \0 (which is <B>not</B> included in the encoded
+        * length).
+        *
+        * @param b The array from whcih to copy bytes.
+        * @param off The offset into the array at which to start copying
+        * @param len The number of bytes to copy.  
+        */
+       public void appendBytes( byte b[], int off, int numBytes ) {
+           appendInt( numBytes );
+           if( pos + numBytes > buff.length ) {
+               System.out.println("Buffer overflow " + buff.length + " " + pos + " " 
++ numBytes );
+               // XXX Log
            }
-           System.arraycopy( b, off, buff, pos, len);
-           buff[pos+len]=0;
-           pos+=len;
-           pos++;
+           System.arraycopy( b, off, buff, pos, numBytes);
+           buff[pos + numBytes] = 0; // Terminating \0
+           pos += numBytes + 1;
        }
 
+       
+       // ============ Data Reading Methods ===================
+
+       /**
+        * Read an integer from packet, and advance the read position past
+        * it.  Integers are encoded as two unsigned bytes with the
+        * high-order byte first, and, as far as I can tell, in
+        * little-endian order within each byte.  
+        */
        public int getInt() {
-           int res=BuffTool.getInt( buff, pos );
-           pos+=2;
-           return res;
+           int result = peekInt();
+           pos += 2;
+           return result;
        }
 
+       /**
+        * Read an integer from the packet, but don't advance the read
+        * position past it.  
+        */
        public int peekInt() {
-           return BuffTool.getInt( buff, pos );
+           int b1 = buff[pos] & 0xFF;  // No swap, Java order
+           int b2 = buff[pos + 1] & 0xFF;
+
+           return  (b1<<8) + b2;
        }
 
        public byte getByte() {
@@ -606,35 +805,51 @@
            return buff[pos];
        }
 
+       public static final String DEFAULT_CHAR_ENCODING = "8859_1";
+
+       /**
+        * Read a String from the packet, and advance the read position
+        * past it.  See appendString for details on string encoding.
+        **/
        public String getString() throws java.io.UnsupportedEncodingException {
-           int ll= getInt();
-           if( (ll == 0xFFFF) || (ll==-1) ) {
-               //          System.out.println("null string " + ll);
+           int length = getInt();
+           if( (length == 0xFFFF) || (length == -1) ) {
+               //          System.out.println("null string " + length);
                return null;
            }
-           String s=BuffTool.getString( buff, pos, ll );
-           pos +=ll;
-           pos++;
+           String s = new String( buff, pos, length, DEFAULT_CHAR_ENCODING );
+
+           pos += length;
+           pos++; // Skip the terminating \0
            return s;
        }
 
+       /**
+        * Copy a chunk of bytes from the packet into an array and advance
+        * the read position past the chunk.  See appendBytes() for details
+        * on the encoding.
+        *
+        * @return The number of bytes copied.
+        */
        public int getBytes(byte dest[]) {
-           int ll= getInt();
-           if( ll > buff.length ) {
+           int length = getInt();
+           if( length > buff.length ) {
+               // XXX Should be if(pos + length > buff.legth)?
                System.out.println("XXX Assert failed, buff too small ");
            }
        
-           if( (ll == 0xFFFF) || (ll==-1) ) {
-               System.out.println("null string " + ll);
+           if( (length == 0xFFFF) || (length == -1) ) {
+               System.out.println("null string " + length);
                return 0;
            }
 
-           System.arraycopy( buff, pos,  dest, 0, ll );
-           pos +=ll;
-           pos++; // ??? 
-           return ll;
+           System.arraycopy( buff, pos,  dest, 0, length );
+           pos += length;
+           pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
+           return length;
        }
 
+       // ============== Debugging code =========================
        private String hex( int x ) {
            //      if( x < 0) x=256 + x;
            String h=Integer.toHexString( x );
@@ -657,7 +872,7 @@
        }
     
        public void dump(String msg) {
-           System.out.println( msg + ": " + buff + " " + pos +"/" + len + "/" + 
maxlen );
+           System.out.println( msg + ": " + buff + " " + pos +"/" + len);
 
            for( int j=0; j<len + 16; j+=16 )
                hexLine( j );
@@ -665,6 +880,4 @@
            System.out.println();
        }
     }
-
-
 }

Reply via email to