--- /dev/null
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation. Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL. If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.io.Reader;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * An input buffer that combines fast character-based access with\r
+ * (slower) support for retrieving the text of the current line. It\r
+ * also supports building strings directly out of the internal buffer\r
+ * to support fast scanning with minimal object creation.\r
+ *\r
+ * Note that it is customized in several ways to support the\r
+ * TokenStream class, and should not be considered general.\r
+ *\r
+ * Credits to Kipp Hickman and John Bandhauer.\r
+ *\r
+ * @author Mike McCabe\r
+ */\r
+final class LineBuffer {\r
+ /*\r
+ * for smooth operation of getLine(), this should be greater than\r
+ * the length of any expected line. Currently, 256 is 3% slower\r
+ * than 4096 for large compiles, but seems safer given evaluateString.\r
+ * Strings for the scanner are are built with StringBuffers\r
+ * instead of directly out of the buffer whenever a string crosses\r
+ * a buffer boundary, so small buffer sizes will mean that more\r
+ * objects are created.\r
+ */\r
+ static final int BUFLEN = 256;\r
+\r
+ LineBuffer(Reader in, int lineno) {\r
+ this.in = in;\r
+ this.lineno = lineno;\r
+ }\r
+\r
+ int read() throws IOException {\r
+ for(;;) {\r
+ if (end == offset && !fill())\r
+ return -1;\r
+\r
+ // Do only a bitmask + branch per character, at the cost of\r
+ // three branches per low-bits-only (or 2028/9) character.\r
+ if ((buffer[offset] & '\udfd0') == 0) {\r
+ if (buffer[offset] == '\r') {\r
+ // if the next character is a newline, skip past it.\r
+ if ((offset + 1) < end) {\r
+ if (buffer[offset + 1] == '\n')\r
+ offset++;\r
+ } else {\r
+ // set a flag for fill(), in case the first char of the\r
+ // next fill is a newline.\r
+ lastWasCR = true;\r
+ }\r
+ }\r
+ else \r
+ if ((buffer[offset] != '\n') \r
+ && (buffer[offset] != '\u2028')\r
+ && (buffer[offset] != '\u2029'))\r
+ { \r
+ if (Character.getType(buffer[offset])\r
+ == Character.FORMAT) {\r
+ hadCFSinceStringStart = true;\r
+ offset++;\r
+ continue;\r
+ }\r
+ return (int) buffer[offset++];\r
+ }\r
+ offset++;\r
+ prevStart = lineStart;\r
+ lineStart = offset;\r
+ lineno++;\r
+ return '\n';\r
+ }\r
+ if ((buffer[offset] >= 128) \r
+ && (Character.getType(buffer[offset]) == Character.FORMAT)) {\r
+ hadCFSinceStringStart = true;\r
+ offset++;\r
+ }\r
+ else\r
+ break;\r
+ }\r
+ \r
+ return (int) buffer[offset++];\r
+ }\r
+\r
+ void unread() {\r
+ if (offset == 0)\r
+ // We can get here when we're asked to unread() an\r
+ // implicit EOF_CHAR.\r
+ \r
+ // This would also be wrong behavior in the general case,\r
+ // because a peek() could map a buffer.length offset to 0\r
+ // in the process of a fill(), and leave it there. But\r
+ // the scanner never calls peek() or a failed match()\r
+ // followed by unread()... this would violate 1-character\r
+ // lookahead. So we're OK.\r
+ return;\r
+ offset--;\r
+ if ((buffer[offset] & '\ufff0') == 0\r
+ && (buffer[offset] == '\r' || buffer[offset] == '\n')) {\r
+ // back off from the line start we presumably just registered...\r
+ lineStart = prevStart;\r
+ lineno--;\r
+ }\r
+ }\r
+\r
+ int peek() throws IOException {\r
+ if (end == offset && !fill())\r
+ return -1;\r
+\r
+ if (buffer[offset] == '\r')\r
+ return '\n';\r
+\r
+ return buffer[offset];\r
+ }\r
+\r
+ boolean match(char c) throws IOException {\r
+ if (end == offset && !fill())\r
+ return false;\r
+\r
+ // This'd be a place where we'd need to map '\r' to '\n' and\r
+ // do other updates, but TokenStream never looks ahead for\r
+ // '\n', so we don't bother.\r
+ if (buffer[offset] == c) {\r
+ offset++;\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ // Reconstruct a source line from the buffers. This can be slow...\r
+ String getLine() {\r
+ StringBuffer result = new StringBuffer();\r
+\r
+ int start = lineStart;\r
+ if (start >= offset) {\r
+ // the line begins somewhere in the other buffer; get that first.\r
+ if (otherStart < otherEnd)\r
+ // if a line ending was seen in the other buffer... otherwise\r
+ // just ignore this strange case.\r
+ result.append(otherBuffer, otherStart,\r
+ otherEnd - otherStart);\r
+ start = 0;\r
+ }\r
+\r
+ // get the part of the line in the current buffer.\r
+ result.append(buffer, start, offset - start);\r
+\r
+ // Get the remainder of the line.\r
+ int i = offset;\r
+ while(true) {\r
+ if (i == buffer.length) {\r
+ // we're out of buffer, let's just expand it. We do\r
+ // this instead of reading into a StringBuffer to\r
+ // preserve the stream for later reads.\r
+ char[] newBuffer = new char[buffer.length * 2];\r
+ System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);\r
+ buffer = newBuffer;\r
+ int charsRead = 0;\r
+ try {\r
+ charsRead = in.read(buffer, end, buffer.length - end);\r
+ } catch (IOException ioe) {\r
+ // ignore it, we're already displaying an error...\r
+ }\r
+ if (charsRead < 0)\r
+ break;\r
+ end += charsRead;\r
+ }\r
+ if (buffer[i] == '\r' || buffer[i] == '\n')\r
+ break;\r
+ i++;\r
+ }\r
+\r
+ result.append(buffer, offset, i - offset);\r
+ return result.toString();\r
+ }\r
+\r
+ // Get the offset of the current character, relative to\r
+ // the line that getLine() returns.\r
+ int getOffset() {\r
+ if (lineStart >= offset)\r
+ // The line begins somewhere in the other buffer.\r
+ return offset + (otherEnd - otherStart);\r
+ else\r
+ return offset - lineStart;\r
+ }\r
+\r
+ // Set a mark to indicate that the reader should begin\r
+ // accumulating characters for getString(). The string begins\r
+ // with the last character read.\r
+ void startString() {\r
+ if (offset == 0) {\r
+ // We can get here if startString is called after a peek()\r
+ // or failed match() with offset past the end of the\r
+ // buffer.\r
+\r
+ // We're at the beginning of the buffer, and the previous character\r
+ // (which we want to include) is at the end of the last one, so\r
+ // we just go to StringBuffer mode.\r
+ stringSoFar = new StringBuffer();\r
+ \r
+ stringSoFar.append(otherBuffer, otherEnd - 1, 1);\r
+\r
+ stringStart = -1; // Set sentinel value.\r
+ hadCFSinceStringStart = ((otherBuffer[otherEnd - 1] >= 128) \r
+ && Character.getType(otherBuffer[otherEnd - 1])\r
+ == Character.FORMAT);\r
+ } else {\r
+ // Support restarting strings\r
+ stringSoFar = null;\r
+ stringStart = offset - 1;\r
+ hadCFSinceStringStart = ((buffer[stringStart] >= 128) \r
+ && Character.getType(buffer[stringStart]) == Character.FORMAT);\r
+ }\r
+ \r
+ }\r
+\r
+ // Get a string consisting of the characters seen since the last\r
+ // startString.\r
+ String getString() {\r
+ String result;\r
+\r
+ /*\r
+ * There's one strange case here: If the character offset currently\r
+ * points to (which we never want to include in the string) is\r
+ * a newline, then if the previous character is a carriage return,\r
+ * we probably want to exclude that as well. If the offset is 0,\r
+ * then we hope that fill() handled excluding it from stringSoFar.\r
+ */\r
+ int loseCR = (offset > 0 &&\r
+ buffer[offset] == '\n' && buffer[offset - 1] == '\r') ?\r
+ 1 : 0;\r
+\r
+ if (stringStart != -1) {\r
+ // String mark is valid, and in this buffer.\r
+\r
+ result = new String(buffer, stringStart, \r
+ offset - stringStart - loseCR);\r
+ } else {\r
+ if (stringSoFar == null) \r
+ stringSoFar = new StringBuffer();\r
+ // Exclude cr as well as nl of newline. If offset is 0, then\r
+ // hopefully fill() did the right thing.\r
+ result = (stringSoFar.append(buffer, 0, offset - loseCR)).toString();\r
+ }\r
+ \r
+ stringStart = -1;\r
+ stringSoFar = null;\r
+ \r
+ if (hadCFSinceStringStart) {\r
+ char c[] = result.toCharArray();\r
+ StringBuffer x = null;\r
+ for (int i = 0; i < c.length; i++) {\r
+ if (Character.getType(c[i]) == Character.FORMAT) {\r
+ if (x == null) {\r
+ x = new StringBuffer();\r
+ x.append(c, 0, i);\r
+ }\r
+ }\r
+ else\r
+ if (x != null) x.append(c[i]);\r
+ }\r
+ if (x != null) result = x.toString(); \r
+ }\r
+ \r
+ return result;\r
+ } \r
+\r
+ boolean fill() throws IOException {\r
+ // not sure I care...\r
+ if (end - offset != 0) \r
+ throw new IOException("fill of non-empty buffer");\r
+\r
+ // If there's a string currently being accumulated, save\r
+ // off the progress.\r
+\r
+ /*\r
+ * Exclude an end-of-buffer carriage return. NOTE this is not\r
+ * fully correct in the general case, because we really only\r
+ * want to exclude the carriage return if it's followed by a\r
+ * linefeed at the beginning of the next buffer. But we fudge\r
+ * because the scanner doesn't do this.\r
+ */\r
+ int loseCR = (offset > 0 && lastWasCR) ? 1 : 0;\r
+\r
+ if (stringStart != -1) {\r
+ // The mark is in the current buffer, save off from the mark to the\r
+ // end.\r
+ stringSoFar = new StringBuffer();\r
+\r
+ stringSoFar.append(buffer, stringStart, end - stringStart - loseCR);\r
+ stringStart = -1;\r
+ } else if (stringSoFar != null) {\r
+ // the string began prior to the current buffer, so save the\r
+ // whole current buffer.\r
+ stringSoFar.append(buffer, 0, end - loseCR);\r
+ }\r
+\r
+ // swap buffers\r
+ char[] tempBuffer = buffer;\r
+ buffer = otherBuffer;\r
+ otherBuffer = tempBuffer;\r
+\r
+ // allocate the buffers lazily, in case we're handed a short string.\r
+ if (buffer == null) {\r
+ buffer = new char[BUFLEN];\r
+ }\r
+\r
+ // buffers have switched, so move the newline marker.\r
+ otherStart = lineStart;\r
+ otherEnd = end;\r
+\r
+ // set lineStart to a sentinel value, unless this is the first\r
+ // time around.\r
+ prevStart = lineStart = (otherBuffer == null) ? 0 : buffer.length + 1;\r
+ \r
+ offset = 0;\r
+ end = in.read(buffer, 0, buffer.length);\r
+ if (end < 0) {\r
+ end = 0;\r
+\r
+ // can't null buffers here, because a string might be retrieved\r
+ // out of the other buffer, and a 0-length string might be\r
+ // retrieved out of this one.\r
+\r
+ hitEOF = true;\r
+ return false;\r
+ }\r
+\r
+ // If the last character of the previous fill was a carriage return,\r
+ // then ignore a newline.\r
+\r
+ // There's another bizzare special case here. If lastWasCR is\r
+ // true, and we see a newline, and the buffer length is\r
+ // 1... then we probably just read the last character of the\r
+ // file, and returning after advancing offset is not the right\r
+ // thing to do. Instead, we try to ignore the newline (and\r
+ // likely get to EOF for real) by doing yet another fill().\r
+ if (lastWasCR) {\r
+ if (buffer[0] == '\n') {\r
+ offset++;\r
+ if (end == 1)\r
+ return fill();\r
+ }\r
+ lineStart = offset;\r
+ lastWasCR = false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ int getLineno() { return lineno; }\r
+ boolean eof() { return hitEOF; }\r
+ \r
+ private Reader in;\r
+ private char[] otherBuffer = null;\r
+ private char[] buffer = null;\r
+\r
+ // Yes, there are too too many of these.\r
+ private int offset = 0;\r
+ private int end = 0;\r
+ private int otherEnd;\r
+ private int lineno;\r
+\r
+ private int lineStart = 0;\r
+ private int otherStart = 0;\r
+ private int prevStart = 0;\r
+ \r
+ private boolean lastWasCR = false;\r
+ private boolean hitEOF = false;\r
+\r
+ private int stringStart = -1;\r
+ private StringBuffer stringSoFar = null;\r
+ private boolean hadCFSinceStringStart = false;\r
+\r
+}\r
+\r
+\r