Merge pull request #3 from square/jwilson_0810_indexOfElement

Use indexOfElement in JsonReader.
This commit is contained in:
Jake Wharton
2014-08-09 22:43:43 -07:00
4 changed files with 88 additions and 115 deletions

View File

@@ -16,7 +16,6 @@
<dependency> <dependency>
<groupId>com.squareup.okio</groupId> <groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId> <artifactId>okio</artifactId>
<version>1.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>

View File

@@ -20,6 +20,7 @@ import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import okio.Buffer; import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString;
import okio.Source; import okio.Source;
/** /**
@@ -176,6 +177,12 @@ import okio.Source;
public class JsonReader implements Closeable { public class JsonReader implements Closeable {
private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10; private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;
private static final ByteString SINGLE_QUOTE_OR_SLASH = ByteString.encodeUtf8("'\\");
private static final ByteString DOUBLE_QUOTE_OR_SLASH = ByteString.encodeUtf8("\"\\");
private static final ByteString UNQUOTED_STRING_TERMINALS
= ByteString.encodeUtf8("{}[]:, \n\t\r\f/\\;#=");
private static final ByteString LINEFEED_OR_CARRIAGE_RETURN = ByteString.encodeUtf8("\n\r");
private static final int PEEKED_NONE = 0; private static final int PEEKED_NONE = 0;
private static final int PEEKED_BEGIN_OBJECT = 1; private static final int PEEKED_BEGIN_OBJECT = 1;
private static final int PEEKED_END_OBJECT = 2; private static final int PEEKED_END_OBJECT = 2;
@@ -271,7 +278,7 @@ public class JsonReader implements Closeable {
*/ */
public JsonReader(String s) { public JsonReader(String s) {
this.source = new Buffer().writeUtf8(s); this.source = new Buffer().writeUtf8(s);
this.buffer = new Buffer(); this.buffer = source.buffer();
} }
/** /**
@@ -561,7 +568,7 @@ public class JsonReader implements Closeable {
buffer.readByte(); // Consume '['. buffer.readByte(); // Consume '['.
return peeked = PEEKED_BEGIN_ARRAY; return peeked = PEEKED_BEGIN_ARRAY;
case '{': case '{':
buffer.readByte(); // Consume ']'. buffer.readByte(); // Consume '{'.
return peeked = PEEKED_BEGIN_OBJECT; return peeked = PEEKED_BEGIN_OBJECT;
default: default:
} }
@@ -760,10 +767,10 @@ public class JsonReader implements Closeable {
String result; String result;
if (p == PEEKED_UNQUOTED_NAME) { if (p == PEEKED_UNQUOTED_NAME) {
result = nextUnquotedValue(); result = nextUnquotedValue();
} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
result = nextQuotedValue('\'');
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) { } else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
result = nextQuotedValue('"'); result = nextQuotedValue(DOUBLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
result = nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
} else { } else {
throw new IllegalStateException("Expected a name but was " + peek() throw new IllegalStateException("Expected a name but was " + peek()
+ " at path " + getPath()); + " at path " + getPath());
@@ -789,10 +796,10 @@ public class JsonReader implements Closeable {
String result; String result;
if (p == PEEKED_UNQUOTED) { if (p == PEEKED_UNQUOTED) {
result = nextUnquotedValue(); result = nextUnquotedValue();
} else if (p == PEEKED_SINGLE_QUOTED) {
result = nextQuotedValue('\'');
} else if (p == PEEKED_DOUBLE_QUOTED) { } else if (p == PEEKED_DOUBLE_QUOTED) {
result = nextQuotedValue('"'); result = nextQuotedValue(DOUBLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_SINGLE_QUOTED) {
result = nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_BUFFERED) { } else if (p == PEEKED_BUFFERED) {
result = peekedString; result = peekedString;
peekedString = null; peekedString = null;
@@ -878,8 +885,10 @@ public class JsonReader implements Closeable {
if (p == PEEKED_NUMBER) { if (p == PEEKED_NUMBER) {
peekedString = buffer.readUtf8(peekedNumberLength); peekedString = buffer.readUtf8(peekedNumberLength);
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { } else if (p == PEEKED_DOUBLE_QUOTED) {
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); peekedString = nextQuotedValue(DOUBLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_SINGLE_QUOTED) {
peekedString = nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_UNQUOTED) { } else if (p == PEEKED_UNQUOTED) {
peekedString = nextUnquotedValue(); peekedString = nextUnquotedValue();
} else if (p != PEEKED_BUFFERED) { } else if (p != PEEKED_BUFFERED) {
@@ -923,8 +932,10 @@ public class JsonReader implements Closeable {
if (p == PEEKED_NUMBER) { if (p == PEEKED_NUMBER) {
peekedString = buffer.readUtf8(peekedNumberLength); peekedString = buffer.readUtf8(peekedNumberLength);
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_SINGLE_QUOTED) {
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); peekedString = p == PEEKED_DOUBLE_QUOTED
? nextQuotedValue(DOUBLE_QUOTE_OR_SLASH)
: nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
try { try {
long result = Long.parseLong(peekedString); long result = Long.parseLong(peekedString);
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@@ -957,109 +968,61 @@ public class JsonReader implements Closeable {
* should have already been read. This consumes the closing quote, but does * should have already been read. This consumes the closing quote, but does
* not include it in the returned string. * not include it in the returned string.
* *
* @param quote either ' or ".
* @throws NumberFormatException if any unicode escape sequences are * @throws NumberFormatException if any unicode escape sequences are
* malformed. * malformed.
*/ */
private String nextQuotedValue(char quote) throws IOException { private String nextQuotedValue(ByteString runTerminator) throws IOException {
StringBuilder builder = new StringBuilder(); StringBuilder builder = null;
int p = 0; while (true) {
while (fillBuffer(p + 1)) { long index = source.indexOfElement(runTerminator);
int c = buffer.getByte(p++); if (index == -1L) throw syntaxError("Unterminated string");
if (c == quote) { // If we've got an escape character, we're going to need a string builder.
builder.append(buffer.readUtf8(p - 1)); if (buffer.getByte(index) == '\\') {
buffer.readByte(); if (builder == null) builder = new StringBuilder();
return builder.toString(); builder.append(buffer.readUtf8(index));
} else if (c == '\\') {
builder.append(buffer.readUtf8(p - 1));
buffer.readByte(); // '\' buffer.readByte(); // '\'
builder.append(readEscapeCharacter()); builder.append(readEscapeCharacter());
p = 0; continue;
}
// If it isn't the escape character, it's the quote. Return the string.
if (builder == null) {
String result = buffer.readUtf8(index);
buffer.readByte(); // Consume the quote character.
return result;
} else {
builder.append(buffer.readUtf8(index));
buffer.readByte(); // Consume the quote character.
return builder.toString();
} }
} }
throw syntaxError("Unterminated string");
} }
/** /** Returns an unquoted value as a string. */
* Returns an unquoted value as a string.
*/
@SuppressWarnings("fallthrough")
private String nextUnquotedValue() throws IOException { private String nextUnquotedValue() throws IOException {
int i = 0; long i = source.indexOfElement(UNQUOTED_STRING_TERMINALS);
return i != -1 ? buffer.readUtf8(i) : buffer.readUtf8();
findNonStringChar:
for (; fillBuffer(i + 1); i++) {
switch (buffer.getByte(i)) {
case '/':
case '\\':
case ';':
case '#':
case '=':
checkLenient(); // fall-through
case '{':
case '}':
case '[':
case ']':
case ':':
case ',':
case ' ':
case '\t':
case '\f':
case '\r':
case '\n':
break findNonStringChar;
}
}
return buffer.readUtf8(i);
} }
private void skipQuotedValue(char quote) throws IOException { private void skipQuotedValue(ByteString runTerminator) throws IOException {
int p = 0; while (true) {
while (fillBuffer(p + 1)) { long index = source.indexOfElement(runTerminator);
int c = buffer.getByte(p++); if (index == -1L) throw syntaxError("Unterminated string");
if (c == quote) {
buffer.skip(p); if (buffer.getByte(index) == '\\') {
return; buffer.skip(index + 1);
} else if (c == '\\') {
buffer.skip(p);
readEscapeCharacter(); readEscapeCharacter();
p = 0; } else {
buffer.skip(index + 1);
return;
} }
} }
throw syntaxError("Unterminated string");
} }
private void skipUnquotedValue() throws IOException { private void skipUnquotedValue() throws IOException {
int i = 0; long i = source.indexOfElement(UNQUOTED_STRING_TERMINALS);
buffer.skip(i != -1L ? i : buffer.size());
findNonStringChar:
for (; fillBuffer(i + 1); i++) {
switch (buffer.getByte(i)) {
case '/':
case '\\':
case ';':
case '#':
case '=':
checkLenient(); // fall-through
case '{':
case '}':
case '[':
case ']':
case ':':
case ',':
case ' ':
case '\t':
case '\f':
case '\r':
case '\n':
break findNonStringChar;
}
}
buffer.skip(i);
} }
/** /**
@@ -1092,8 +1055,10 @@ public class JsonReader implements Closeable {
if (p == PEEKED_NUMBER) { if (p == PEEKED_NUMBER) {
peekedString = buffer.readUtf8(peekedNumberLength); peekedString = buffer.readUtf8(peekedNumberLength);
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_SINGLE_QUOTED) {
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); peekedString = p == PEEKED_DOUBLE_QUOTED
? nextQuotedValue(DOUBLE_QUOTE_OR_SLASH)
: nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
try { try {
result = Integer.parseInt(peekedString); result = Integer.parseInt(peekedString);
peeked = PEEKED_NONE; peeked = PEEKED_NONE;
@@ -1158,10 +1123,10 @@ public class JsonReader implements Closeable {
count--; count--;
} else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) { } else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
skipUnquotedValue(); skipUnquotedValue();
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
skipQuotedValue('\'');
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) { } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
skipQuotedValue('"'); skipQuotedValue(DOUBLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
skipQuotedValue(SINGLE_QUOTE_OR_SLASH);
} else if (p == PEEKED_NUMBER) { } else if (p == PEEKED_NUMBER) {
buffer.skip(peekedNumberLength); buffer.skip(peekedNumberLength);
} }
@@ -1193,10 +1158,7 @@ public class JsonReader implements Closeable {
* false. * false.
*/ */
private boolean fillBuffer(int minimum) throws IOException { private boolean fillBuffer(int minimum) throws IOException {
while (buffer.size() < minimum) { return source.request(minimum);
if (source.read(buffer, 2048) == -1) return false;
}
return true;
} }
/** /**
@@ -1285,12 +1247,8 @@ public class JsonReader implements Closeable {
* caller. * caller.
*/ */
private void skipToEndOfLine() throws IOException { private void skipToEndOfLine() throws IOException {
while (fillBuffer(1)) { long index = source.indexOfElement(LINEFEED_OR_CARRIAGE_RETURN);
byte c = buffer.readByte(); buffer.skip(index != -1 ? index + 1 : buffer.size());
if (c == '\n' || c == '\r') {
break;
}
}
} }
/** /**

View File

@@ -17,8 +17,6 @@ package com.squareup.moshi;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays; import java.util.Arrays;
import okio.Source; import okio.Source;
import org.junit.Ignore; import org.junit.Ignore;
@@ -895,6 +893,11 @@ public final class JsonReaderTest {
reader.setLenient(true); reader.setLenient(true);
reader.beginArray(); reader.beginArray();
assertEquals(true, reader.nextBoolean()); assertEquals(true, reader.nextBoolean());
reader = new JsonReader("a//");
reader.setLenient(true);
assertEquals("a", reader.nextString());
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
} }
@Test public void strictCommentsWithSkipValue() throws IOException { @Test public void strictCommentsWithSkipValue() throws IOException {
@@ -1011,6 +1014,14 @@ public final class JsonReaderTest {
assertEquals("a", reader.nextString()); assertEquals("a", reader.nextString());
} }
@Test public void lenientUnquotedStringsDelimitedByComment() throws IOException {
JsonReader reader = new JsonReader("[a#comment\n]");
reader.setLenient(true);
reader.beginArray();
assertEquals("a", reader.nextString());
reader.endArray();
}
@Test public void strictSingleQuotedStrings() throws IOException { @Test public void strictSingleQuotedStrings() throws IOException {
JsonReader reader = new JsonReader("['a']"); JsonReader reader = new JsonReader("['a']");
reader.beginArray(); reader.beginArray();

View File

@@ -26,7 +26,7 @@
<java.version>1.6</java.version> <java.version>1.6</java.version>
<!-- Dependencies --> <!-- Dependencies -->
<okio.version>1.0.1</okio.version> <okio.version>1.0.2-SNAPSHOT</okio.version>
<!-- Test Dependencies --> <!-- Test Dependencies -->
<junit.version>4.11</junit.version> <junit.version>4.11</junit.version>
@@ -58,6 +58,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>${junit.version}</version> <version>${junit.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>${okio.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>