Skip to content

Commit 1c63d72

Browse files
committed
fix: fixes problems with missing or multiple delimiters
A missing delimiter would cause the parser to hang in a loop and cause a OOM error, while multiple delimiters would cause the parser to incorrectly parse the line, Fixes #20
1 parent 5e5162f commit 1c63d72

File tree

1 file changed

+25
-15
lines changed

1 file changed

+25
-15
lines changed

src/main/java/org/codejive/properties/PropertiesParser.java

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import java.io.Reader;
55
import java.util.Objects;
66
import java.util.Spliterators;
7-
import java.util.function.BiFunction;
7+
import java.util.concurrent.atomic.AtomicBoolean;
8+
import java.util.concurrent.atomic.AtomicInteger;
89
import java.util.function.Consumer;
10+
import java.util.function.Function;
911
import java.util.stream.Stream;
1012
import java.util.stream.StreamSupport;
1113

@@ -211,31 +213,36 @@ public Token nextToken() throws IOException {
211213
if (isEof(ch)) {
212214
return null;
213215
}
214-
int oldch = -1;
215-
BiFunction<Integer, Integer, Boolean> isValid = (c, oldc) -> false;
216+
Function<Integer, Boolean> isValid = (c) -> false;
216217
Type nextState = null;
217218
if (state == null) {
218219
if (isCommentChar(ch)) {
219220
state = Type.COMMENT;
220-
isValid = (c, oldc) -> !isEol(c) && !isEof(c);
221-
} else if (isWhitespaceChar(ch)) {
221+
isValid = (c) -> !isEol(c) && !isEof(c);
222+
} else if (isWhitespaceEolChar(ch)) {
222223
state = Type.WHITESPACE;
223-
isValid = (c, oldc) -> isWhitespaceChar(c) && !isEol(oldc);
224+
final AtomicInteger oldc = new AtomicInteger(-1);
225+
isValid = (c) -> isWhitespaceEolChar(c) && !isEol(oldc.getAndSet(c));
224226
} else {
225227
state = Type.KEY;
226-
isValid = (c, oldc) -> !isSeparatorChar(c);
228+
isValid =
229+
(c) ->
230+
!isSeparatorChar(c)
231+
&& !isWhitespaceChar(c)
232+
&& !isEol(c)
233+
&& !isEof(c);
227234
nextState = Type.SEPARATOR;
228235
}
229236
} else if (state == Type.SEPARATOR) {
230-
isValid = (c, oldc) -> isSeparatorChar(c);
237+
final AtomicBoolean once = new AtomicBoolean(true);
238+
isValid = (c) -> isWhitespaceChar(c) || (isSeparatorChar(c) && once.getAndSet(false));
231239
nextState = Type.VALUE;
232240
} else if (state == Type.VALUE) {
233-
isValid = (c, oldc) -> !isEol(c) && !isEof(c);
241+
isValid = (c) -> !isEol(c) && !isEof(c);
234242
}
235243
while (true) {
236-
if (isValid.apply(ch, oldch)) {
244+
if (isValid.apply(ch)) {
237245
addChar(readChar());
238-
oldch = ch;
239246
ch = peekChar();
240247
} else {
241248
String text = string();
@@ -336,8 +343,7 @@ static String unescape(String escape) {
336343
case '\n':
337344
// Skip any leading whitespace
338345
while (i < (escape.length() - 1)
339-
&& isWhitespaceChar(ch = escape.charAt(i + 1))
340-
&& !isEol(ch)) {
346+
&& isWhitespaceChar(ch = escape.charAt(i + 1))) {
341347
i++;
342348
}
343349
break;
@@ -353,11 +359,15 @@ && isWhitespaceChar(ch = escape.charAt(i + 1))
353359
}
354360

355361
private static boolean isSeparatorChar(int ch) {
356-
return ch == ' ' || ch == '\t' || ch == '=' || ch == ':';
362+
return ch == '=' || ch == ':';
357363
}
358364

359365
private static boolean isWhitespaceChar(int ch) {
360-
return ch == ' ' || ch == '\t' || ch == '\f' || isEol(ch);
366+
return ch == ' ' || ch == '\t' || ch == '\f';
367+
}
368+
369+
private static boolean isWhitespaceEolChar(int ch) {
370+
return isWhitespaceChar(ch) || isEol(ch);
361371
}
362372

363373
private static boolean isCommentChar(int ch) {

0 commit comments

Comments
 (0)