diff --git a/src/java.base/share/classes/java/util/Properties.java b/src/java.base/share/classes/java/util/Properties.java index 397ba4450b66d1574ce27f9115ebda3d8515594f..84c9eb27bc9441b958f6c6f565d5841045fdb139 100644 --- a/src/java.base/share/classes/java/util/Properties.java +++ b/src/java.base/share/classes/java/util/Properties.java @@ -404,17 +404,15 @@ class Properties extends Hashtable { load0(new LineReader(inStream)); } - private void load0 (LineReader lr) throws IOException { - char[] convtBuf = new char[1024]; + private void load0(LineReader lr) throws IOException { + StringBuilder outBuffer = new StringBuilder(); int limit; int keyLen; int valueStart; - char c; boolean hasSep; boolean precedingBackslash; while ((limit = lr.readLine()) >= 0) { - c = 0; keyLen = 0; valueStart = limit; hasSep = false; @@ -422,7 +420,7 @@ class Properties extends Hashtable { //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">"); precedingBackslash = false; while (keyLen < limit) { - c = lr.lineBuf[keyLen]; + char c = lr.lineBuf[keyLen]; //need check if escaped. if ((c == '=' || c == ':') && !precedingBackslash) { valueStart = keyLen + 1; @@ -440,7 +438,7 @@ class Properties extends Hashtable { keyLen++; } while (valueStart < limit) { - c = lr.lineBuf[valueStart]; + char c = lr.lineBuf[valueStart]; if (c != ' ' && c != '\t' && c != '\f') { if (!hasSep && (c == '=' || c == ':')) { hasSep = true; @@ -450,8 +448,8 @@ class Properties extends Hashtable { } valueStart++; } - String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf); - String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf); + String key = loadConvert(lr.lineBuf, 0, keyLen, outBuffer); + String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, outBuffer); put(key, value); } } @@ -462,64 +460,56 @@ class Properties extends Hashtable { * Method returns the char length of the "logical line" and stores * the line in "lineBuf". */ - class LineReader { - public LineReader(InputStream inStream) { + private static class LineReader { + LineReader(InputStream inStream) { this.inStream = inStream; inByteBuf = new byte[8192]; } - public LineReader(Reader reader) { + LineReader(Reader reader) { this.reader = reader; inCharBuf = new char[8192]; } - byte[] inByteBuf; - char[] inCharBuf; char[] lineBuf = new char[1024]; - int inLimit = 0; - int inOff = 0; - InputStream inStream; - Reader reader; + private byte[] inByteBuf; + private char[] inCharBuf; + private int inLimit = 0; + private int inOff = 0; + private InputStream inStream; + private Reader reader; int readLine() throws IOException { + // use locals to optimize for interpreted performance int len = 0; - char c = 0; + int off = inOff; + int limit = inLimit; boolean skipWhiteSpace = true; - boolean isCommentLine = false; - boolean isNewLine = true; boolean appendedLineBegin = false; boolean precedingBackslash = false; - boolean skipLF = false; + boolean fromStream = inStream != null; + byte[] byteBuf = inByteBuf; + char[] charBuf = inCharBuf; + char[] lineBuf = this.lineBuf; + char c; while (true) { - if (inOff >= inLimit) { - inLimit = (inStream==null)?reader.read(inCharBuf) - :inStream.read(inByteBuf); - inOff = 0; - if (inLimit <= 0) { - if (len == 0 || isCommentLine) { + if (off >= limit) { + inLimit = limit = fromStream ? inStream.read(byteBuf) + : reader.read(charBuf); + if (limit <= 0) { + if (len == 0) { return -1; } - if (precedingBackslash) { - len--; - } - return len; - } - } - if (inStream != null) { - //The line below is equivalent to calling a - //ISO8859-1 decoder. - c = (char)(inByteBuf[inOff++] & 0xFF); - } else { - c = inCharBuf[inOff++]; - } - if (skipLF) { - skipLF = false; - if (c == '\n') { - continue; + return precedingBackslash ? len - 1 : len; } + off = 0; } + + // (char)(byte & 0xFF) is equivalent to calling a ISO8859-1 decoder. + c = (fromStream) ? (char)(byteBuf[off++] & 0xFF) : charBuf[off++]; + if (skipWhiteSpace) { if (c == ' ' || c == '\t' || c == '\f') { continue; @@ -529,81 +519,106 @@ class Properties extends Hashtable { } skipWhiteSpace = false; appendedLineBegin = false; + } - if (isNewLine) { - isNewLine = false; + if (len == 0) { // Still on a new logical line if (c == '#' || c == '!') { - // Comment, quickly consume the rest of the line, - // resume on line-break and backslash. - if (inStream != null) { - while (inOff < inLimit) { - byte b = inByteBuf[inOff++]; - if (b == '\n' || b == '\r' || b == '\\') { - c = (char)(b & 0xFF); - break; + // Comment, quickly consume the rest of the line + + // When checking for new line characters a range check, + // starting with the higher bound ('\r') means one less + // branch in the common case. + commentLoop: while (true) { + if (fromStream) { + byte b; + while (off < limit) { + b = byteBuf[off++]; + if (b <= '\r' && (b == '\r' || b == '\n')) + break commentLoop; } - } - } else { - while (inOff < inLimit) { - c = inCharBuf[inOff++]; - if (c == '\n' || c == '\r' || c == '\\') { - break; + if (off == limit) { + inLimit = limit = inStream.read(byteBuf); + if (limit <= 0) { // EOF + return -1; + } + off = 0; + } + } else { + while (off < limit) { + c = charBuf[off++]; + if (c <= '\r' && (c == '\r' || c == '\n')) + break commentLoop; + } + if (off == limit) { + inLimit = limit = reader.read(charBuf); + if (limit <= 0) { // EOF + return -1; + } + off = 0; } } } - isCommentLine = true; + skipWhiteSpace = true; + continue; } } if (c != '\n' && c != '\r') { lineBuf[len++] = c; if (len == lineBuf.length) { - int newLength = lineBuf.length * 2; - if (newLength < 0) { - newLength = Integer.MAX_VALUE; + int maxLen = Integer.MAX_VALUE - 8; // VM allocation limit + int newLen = len * 2; + if (newLen < 0 || newLen > maxLen) { // check for under/overflow + newLen = maxLen; } - char[] buf = new char[newLength]; - System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length); - lineBuf = buf; - } - //flip the preceding backslash flag - if (c == '\\') { - precedingBackslash = !precedingBackslash; - } else { - precedingBackslash = false; + if (newLen <= len) { // still not good? last-ditch attempt then + if (len != Integer.MAX_VALUE) { + newLen = len + 1; + } else { + throw new OutOfMemoryError("Required array length too large"); + } + } + lineBuf = new char[newLen]; + System.arraycopy(this.lineBuf, 0, lineBuf, 0, len); + this.lineBuf = lineBuf; } - } - else { + // flip the preceding backslash flag + precedingBackslash = (c == '\\') ? !precedingBackslash : false; + } else { // reached EOL - if (isCommentLine || len == 0) { - isCommentLine = false; - isNewLine = true; + if (len == 0) { skipWhiteSpace = true; - len = 0; continue; } - if (inOff >= inLimit) { - inLimit = (inStream==null) - ?reader.read(inCharBuf) - :inStream.read(inByteBuf); - inOff = 0; - if (inLimit <= 0) { - if (precedingBackslash) { - len--; - } - return len; + if (off >= limit) { + inLimit = limit = fromStream ? inStream.read(byteBuf) + : reader.read(charBuf); + off = 0; + if (limit <= 0) { // EOF + return precedingBackslash ? len - 1 : len; } } if (precedingBackslash) { + // backslash at EOL is not part of the line len -= 1; - //skip the leading whitespace characters in following line + // skip leading whitespace characters in the following line skipWhiteSpace = true; appendedLineBegin = true; precedingBackslash = false; + // take care not to include any subsequent \n if (c == '\r') { - skipLF = true; + if (fromStream) { + if (byteBuf[off] == '\n') { + off++; + } + } else { + if (charBuf[off] == '\n') { + off++; + } + } } } else { + inOff = off; return len; } } @@ -615,18 +630,24 @@ class Properties extends Hashtable { * Converts encoded \uxxxx to unicode chars * and changes special saved chars to their original forms */ - private String loadConvert (char[] in, int off, int len, char[] convtBuf) { - if (convtBuf.length < len) { - int newLen = len * 2; - if (newLen < 0) { - newLen = Integer.MAX_VALUE; - } - convtBuf = new char[newLen]; - } + private String loadConvert(char[] in, int off, int len, StringBuilder out) { char aChar; - char[] out = convtBuf; - int outLen = 0; int end = off + len; + int start = off; + while (off < end) { + aChar = in[off++]; + if (aChar == '\\') { + break; + } + } + if (off == end) { // No backslash + return new String(in, start, len); + } + + // backslash found at off - 1, reset the shared buffer, rewind offset + out.setLength(0); + off--; + out.append(in, start, off - start); while (off < end) { aChar = in[off++]; @@ -654,20 +675,20 @@ class Properties extends Hashtable { throw new IllegalArgumentException( "Malformed \\uxxxx encoding."); } - } - out[outLen++] = (char)value; + } + out.append((char)value); } else { if (aChar == 't') aChar = '\t'; else if (aChar == 'r') aChar = '\r'; else if (aChar == 'n') aChar = '\n'; else if (aChar == 'f') aChar = '\f'; - out[outLen++] = aChar; + out.append(aChar); } } else { - out[outLen++] = aChar; + out.append(aChar); } } - return new String (out, 0, outLen); + return out.toString(); } /*