webwork/src/main/webwork/util FastByteArrayOutputStream.java, 1.4, 1.5

[email protected] Wed, 16 Jan 2008 02:54:07 -0800
Newsgroups gmane.comp.java.open-symphony.cvs
Message-ID <[email protected]>
--===============0742283853==

Update of /cvsroot/opensymphony/webwork/src/main/webwork/util
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv9103/main/webwork/util

Modified Files:
	FastByteArrayOutputStream.java 
Log Message:
Fix encoding so that multi-byte characters don't mess up webwork's tags.

This fixes WW-1495 for the 1.4 webwork branch.

Patch courtesy of Brad Baker from Atlassian.


Index: FastByteArrayOutputStream.java
===================================================================
RCS file: /cvsroot/opensymphony/webwork/src/main/webwork/util/FastByteArrayOutputStream.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- FastByteArrayOutputStream.java	16 Feb 2004 17:31:29 -0000	1.4
+++ FastByteArrayOutputStream.java	16 Jan 2008 10:54:04 -0000	1.5
@@ -6,10 +6,12 @@
  */
 package webwork.util;
 
-import javax.servlet.jsp.JspWriter;
-import java.io.*;
-import java.util.LinkedList;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
 import java.util.Iterator;
+import java.util.LinkedList;
+import javax.servlet.jsp.JspWriter;
 
 /**
  * A speedy implementation of ByteArrayOutputStream. It's not synchronized, and it
@@ -19,200 +21,253 @@
  * @author Rickard �berg
  * @version $Revision$
  */
-public class FastByteArrayOutputStream
-   extends OutputStream
+public class FastByteArrayOutputStream extends OutputStream
 {
-   // Static --------------------------------------------------------
-   private static final int DEFAULT_BLOCK_SIZE = 8192;
+    // Static --------------------------------------------------------
+    private static final int DEFAULT_BLOCK_SIZE = 8192;
 
-   // Attributes ----------------------------------------------------
-   // internal buffer
-   private byte[] buffer;
-   private LinkedList buffers;
-   private int index;
-   private int size;
-   private int blockSize;
-   // is the stream closed?
-   private boolean closed;
+    // Attributes ----------------------------------------------------
+    // internal buffer
+    byte[] buffer;
+    LinkedList buffers;
+    int index;
+    int size;
+    int blockSize;
+    // is the stream closed?
+    boolean closed;
 
-   // Constructors --------------------------------------------------
-   public FastByteArrayOutputStream()
-   {
-      this(DEFAULT_BLOCK_SIZE);
-   }
+    // Constructors --------------------------------------------------
+    public FastByteArrayOutputStream()
+    {
+        this(DEFAULT_BLOCK_SIZE);
+    }
 
-   public FastByteArrayOutputStream(int aSize)
-   {
-      blockSize = aSize;
-      buffer = new byte[blockSize];
-   }
+    public FastByteArrayOutputStream(int aSize)
+    {
+        blockSize = aSize;
+        buffer = new byte[blockSize];
+    }
 
-   // Public
-   public void writeTo(OutputStream out) throws IOException
-   {
-      // Check if we have a list of buffers
-      if (buffers != null)
-      {
-         Iterator iterator = buffers.iterator();
-         while (iterator.hasNext())
-         {
-            byte[] bytes = (byte[]) iterator.next();
-            out.write(bytes, 0, blockSize);
-         }
-      }
+    // Public
+    public void writeTo(OutputStream out) throws IOException
+    {
+        // Check if we have a list of buffers
+        if (buffers != null)
+        {
+            Iterator iterator = buffers.iterator();
+            while (iterator.hasNext())
+            {
+                byte[] bytes = (byte[]) iterator.next();
+                out.write(bytes, 0, blockSize);
+            }
+        }
 
-      // write the internal buffer directly
-      out.write(buffer, 0, index);
-   }
+        // write the internal buffer directly
+        out.write(buffer, 0, index);
+    }
 
-   public void writeTo(RandomAccessFile out) throws IOException
-   {
-      // Check if we have a list of buffers
-      if (buffers != null)
-      {
-         Iterator iterator = buffers.iterator();
-         while (iterator.hasNext())
-         {
-            byte[] bytes = (byte[]) iterator.next();
-            out.write(bytes, 0, blockSize);
-         }
-      }
+    public void writeTo(RandomAccessFile out) throws IOException
+    {
+        // Check if we have a list of buffers
+        if (buffers != null)
+        {
+            Iterator iterator = buffers.iterator();
+            while (iterator.hasNext())
+            {
+                byte[] bytes = (byte[]) iterator.next();
+                out.write(bytes, 0, blockSize);
+            }
+        }
 
-      // write the internal buffer directly
-      out.write(buffer, 0, index);
-   }
+        // write the internal buffer directly
+        out.write(buffer, 0, index);
+    }
 
-   public void writeTo(JspWriter out, String encoding) throws IOException
-   {
-      // Check if we have a list of buffers
-      if (buffers != null)
-      {
-         Iterator iterator = buffers.iterator();
-         while (iterator.hasNext())
-         {
-            byte[] bytes = (byte[]) iterator.next();
+    public void writeTo(final JspWriter out, String encoding) throws IOException
+    {
+        /*
+          There is design tradeoff between being fast, correct and using too much memory when decoding bytes to strings.
 
-            if (encoding != null)
-               out.write(new String(bytes, encoding));
-            else
-               out.write(new String(bytes));
-         }
-      }
+         The rules are thus :
 
-      // write the internal buffer directly
-      if (encoding != null)
-         out.write(new String(buffer, 0, index, encoding));
-      else
-         out.write(new String(buffer, 0, index));
-   }
+         1. if there is only one buffer then its a simple String conversion
 
-   public int getSize()
-   {
-      return size+index;
-   }
+              REASON : Fast!!!
 
-   public byte[] toByteArray()
-   {
-      byte[] data = new byte[getSize()];
+         2. uses full buffer allocation annd System.arrayCopy() to smooosh together the bytes
+              and then use String conversion
 
-      // Check if we have a list of buffers
-      int pos = 0;
-      if (buffers != null)
-      {
-         Iterator iterator = buffers.iterator();
-         while (iterator.hasNext())
-         {
-            byte[] bytes = (byte[]) iterator.next();
-            System.arraycopy(bytes,0,data,pos,blockSize);
-            pos+=blockSize;
-         }
-      }
+              REASON : Fast at the expense of a known amount of memory (eg the used memory * 2)
+        */
+        if (buffers != null)
+        {
+            // RULE 2 : a balance between using some memory and speed
+            writeToViaSmoosh(out, encoding);
+        }
+        else
+        {
+            // RULE 1 : fastest!
+            writeToViaString(out, encoding);
+        }
+    }
 
-      // write the internal buffer directly
-      System.arraycopy(buffer, 0, data, pos, index);
+    /**
+     * This can ONLY be called if there is only a single buffer to write.
+     *
+     * @param out      the JspWriter
+     * @param encoding the encoding
+     * @throws IOException
+     */
+    void writeToViaString(JspWriter out, String encoding) throws IOException
+    {
+        byte[] bufferToWrite = buffer; // this is always the last buffer to write
+        int bufferToWriteLen = index;  // index points to our place in the last buffer
+        writeToImpl(out, encoding, bufferToWrite, bufferToWriteLen);
+    }
 
-      return data;
-   }
+    void writeToViaSmoosh(JspWriter out, String encoding) throws IOException
+    {
+        byte[] bufferToWrite = toByteArray();
+        int bufferToWriteLen = bufferToWrite.length;
+        writeToImpl(out, encoding, bufferToWrite, bufferToWriteLen);
+    }
 
-   public String toString()
-   {
-      return new String(toByteArray());
-   }
+    private void writeToImpl(JspWriter out, String encoding, byte[] bufferToWrite, int bufferToWriteLen)
+            throws IOException
+    {
+        String writeStr;
+        if (encoding != null)
+        {
+            writeStr = new String(bufferToWrite, 0, bufferToWriteLen, encoding);
+        }
+        else
+        {
+            writeStr = new String(bufferToWrite, 0, bufferToWriteLen);
+        }
+        out.write(writeStr);
+    }
 
-   /**
-   * Create a new buffer and store the
-   * current one in linked list
-   */
-   protected void addBuffer()
-   {
-      if (buffers == null)
-         buffers = new LinkedList();
+    public int getSize()
+    {
+        return size + index;
+    }
 
-      buffers.addLast(buffer);
+    public byte[] toByteArray()
+    {
+        byte[] data = new byte[getSize()];
 
-      buffer = new byte[blockSize];
-      size += index;
-      index = 0;
-   }
+        // Check if we have a list of buffers
+        int pos = 0;
+        if (buffers != null)
+        {
+            Iterator iterator = buffers.iterator();
+            while (iterator.hasNext())
+            {
+                byte[] bytes = (byte[]) iterator.next();
+                System.arraycopy(bytes, 0, data, pos, blockSize);
+                pos += blockSize;
+            }
+        }
 
-   // OutputStream overrides ----------------------------------------
-   public void write(int datum) throws IOException
-   {
-      if (closed)
-      {
-         throw new IOException("Stream closed");
-      } else
-      {
-         if (index == blockSize)
-            addBuffer();
+        // write the internal buffer directly
+        System.arraycopy(buffer, 0, data, pos, index);
 
-         // store the byte
-         buffer[index++] = (byte) datum;
-      }
-   }
+        return data;
+    }
 
-   public void write(byte[] data, int offset, int length)
-      throws IOException
-   {
-      if (data == null)
-      {
-         throw new NullPointerException();
-      } else if ((offset < 0) || (offset + length > data.length)
-         || (length < 0))
-      {
-         throw new IndexOutOfBoundsException();
-      } else if (closed)
-      {
-         throw new IOException("Stream closed");
-      } else
-      {
-         if (index + length > blockSize)
-         {
-            int copyLength;
-            do
+    public String toString()
+    {
+        return new String(toByteArray());
+    }
+
+    /**
+     * Create a new buffer and store the
+     * current one in linked list
+     */
+    protected void addBuffer()
+    {
+        if (buffers == null)
+        {
+            buffers = new LinkedList();
+        }
+
+        buffers.addLast(buffer);
+
+        buffer = new byte[blockSize];
+        size += index;
+        index = 0;
+    }
+
+    // OutputStream overrides ----------------------------------------
+    public void write(int datum) throws IOException
+    {
+        if (closed)
+        {
+            throw new IOException("Stream closed");
+        }
+        else
+        {
+            if (index == blockSize)
             {
-               if (index==blockSize)
-                  addBuffer();
+                addBuffer();
+            }
 
-               copyLength = blockSize-index;
-               if (length < copyLength)
-                  copyLength = length;
-               System.arraycopy(data, offset, buffer, index, copyLength);
-               offset += copyLength;
-               index += copyLength;
-               length -= copyLength;
-            } while (length > 0);
-         } else
-         {
-            // Copy in the subarray
-            System.arraycopy(data, offset, buffer, index, length);
-            index += length;
-         }
-      }
-   }
+            // store the byte
+            buffer[index++] = (byte) datum;
+        }
+    }
 
-   public void close()
-   {
-      closed = true;
-   }
+    public void write(byte[] data, int offset, int length)
+            throws IOException
+    {
+        if (data == null)
+        {
+            throw new NullPointerException();
+        }
+        else if ((offset < 0) || (offset + length > data.length)
+                 || (length < 0))
+        {
+            throw new IndexOutOfBoundsException();
+        }
+        else if (closed)
+        {
+            throw new IOException("Stream closed");
+        }
+        else
+        {
+            if (index + length > blockSize)
+            {
+                int copyLength;
+                do
+                {
+                    if (index == blockSize)
+                    {
+                        addBuffer();
+                    }
+
+                    copyLength = blockSize - index;
+                    if (length < copyLength)
+                    {
+                        copyLength = length;
+                    }
+                    System.arraycopy(data, offset, buffer, index, copyLength);
+                    offset += copyLength;
+                    index += copyLength;
+                    length -= copyLength;
+                }
+                while (length > 0);
+            }
+            else
+            {
+                // Copy in the subarray
+                System.arraycopy(data, offset, buffer, index, length);
+                index += length;
+            }
+        }
+    }
+
+    public void close()
+    {
+        closed = true;
+    }
 }



--===============0742283853==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
--===============0742283853==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Opensymphony-cvsmail mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensymphony-cvsmail

--===============0742283853==--