--- a/src/java.base/share/classes/java/util/Properties.java Thu May 23 08:57:24 2019 -0700
+++ b/src/java.base/share/classes/java/util/Properties.java Thu May 23 18:43:47 2019 +0200
@@ -48,6 +48,7 @@
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
+import jdk.internal.util.ArraysSupport;
import jdk.internal.util.xml.PropertiesDefaultHandler;
/**
@@ -404,17 +405,15 @@
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 +421,7 @@
//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 +439,7 @@
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 +449,8 @@
}
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 +461,56 @@
* 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;
+ return precedingBackslash ? len - 1 : len;
}
+ off = 0;
}
- 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;
- }
- }
+
+ // (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 +520,94 @@
}
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;
- }
- char[] buf = new char[newLength];
- System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
- lineBuf = buf;
+ lineBuf = new char[ArraysSupport.newLength(len, 1, len)];
+ System.arraycopy(this.lineBuf, 0, lineBuf, 0, len);
+ this.lineBuf = lineBuf;
}
- //flip the preceding backslash flag
- if (c == '\\') {
- precedingBackslash = !precedingBackslash;
- } else {
- precedingBackslash = false;
- }
- }
- 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 +619,24 @@
* 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;
+ private String loadConvert(char[] in, int off, int len, StringBuilder out) {
+ char aChar;
+ int end = off + len;
+ int start = off;
+ while (off < end) {
+ aChar = in[off++];
+ if (aChar == '\\') {
+ break;
}
- convtBuf = new char[newLen];
+ }
+ if (off == end) { // No backslash
+ return new String(in, start, len);
}
- char aChar;
- char[] out = convtBuf;
- int outLen = 0;
- int end = off + 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 +664,20 @@
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();
}
/*