Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consume less memory #159

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/main/java/com/jsoniter/IterImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@
import com.jsoniter.spi.Slice;

import java.io.IOException;
import java.math.BigInteger;

class IterImpl {

private static BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
private static BigInteger minLong = BigInteger.valueOf(Long.MIN_VALUE);
private static BigInteger maxInt = BigInteger.valueOf(Integer.MAX_VALUE);
private static BigInteger minInt = BigInteger.valueOf(Integer.MIN_VALUE);

public static final int readObjectFieldAsHash(JsonIterator iter) throws IOException {
if (readByte(iter) != '"') {
if (nextToken(iter) != '"') {
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/jsoniter/IterImplNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

class IterImplNumber {

final static int[] intDigits = new int[127];
final static int[] floatDigits = new int[127];
final static byte[] intDigits = new byte[127];
final static byte[] floatDigits = new byte[127];
final static int END_OF_NUMBER = -2;
final static int DOT_IN_NUMBER = -3;
final static int INVALID_CHAR_FOR_NUMBER = -1;
Expand All @@ -51,8 +51,8 @@ class IterImplNumber {
intDigits[i] = INVALID_CHAR_FOR_NUMBER;
}
for (int i = '0'; i <= '9'; ++i) {
floatDigits[i] = (i - '0');
intDigits[i] = (i - '0');
floatDigits[i] = (byte) (i - '0');
intDigits[i] = (byte) (i - '0');
}
floatDigits[','] = END_OF_NUMBER;
floatDigits[']'] = END_OF_NUMBER;
Expand Down
33 changes: 16 additions & 17 deletions src/main/java/com/jsoniter/IterImplString.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,14 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

class IterImplString {

final static int[] hexDigits = new int['f' + 1];

static {
for (int i = 0; i < hexDigits.length; i++) {
hexDigits[i] = -1;
}
for (int i = '0'; i <= '9'; ++i) {
hexDigits[i] = (i - '0');
}
for (int i = 'a'; i <= 'f'; ++i) {
hexDigits[i] = ((i - 'a') + 10);
}
for (int i = 'A'; i <= 'F'; ++i) {
hexDigits[i] = ((i - 'A') + 10);
}
}
private final static byte[] hexDigits = {
/* 48 unused characters skipped */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // decimal digits
-1, -1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15, // hexadecimal digits, lowercase
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15 // hexadecimal digits, uppercase
};

public static final String readString(JsonIterator iter) throws IOException {
byte c = IterImpl.nextToken(iter);
Expand Down Expand Up @@ -95,10 +87,17 @@ private static int parse(JsonIterator iter) throws IOException {
}

public static int translateHex(final byte b) {
int val = hexDigits[b];
int val;
try {
val = hexDigits[b - '0'];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IndexOutOfBoundsException(b + " is not valid hex digit");
}

if (val == -1) {
throw new IndexOutOfBoundsException(b + " is not valid hex digit");
}

return val;
}

Expand Down
110 changes: 65 additions & 45 deletions src/main/java/com/jsoniter/JsonIterator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,44 @@

public class JsonIterator implements Closeable {

// We have our own constants and a dictionary of size 256 bytes,
// while ValueType[256] is 1024 bytes on 32-bit system.
// If no one will call `whatIsNext`, ValueType class won't ever be loaded.
private static final byte T_INVALID = -1;
private static final byte T_STRING = 1;
private static final byte T_NUMBER = 2;
private static final byte T_NULL = 0;
private static final byte T_BOOLEAN = 3;
private static final byte T_ARRAY = 4;
private static final byte T_OBJECT = 5;
private static final byte[] types;
static {
byte[] _types = new byte[256];
for (int i = 0; i < _types.length; i++) {
_types[i] = T_INVALID;
}
_types['"'] = T_STRING;
_types['-'] = T_NUMBER;
_types['0'] = T_NUMBER;
_types['1'] = T_NUMBER;
_types['2'] = T_NUMBER;
_types['3'] = T_NUMBER;
_types['4'] = T_NUMBER;
_types['5'] = T_NUMBER;
_types['6'] = T_NUMBER;
_types['7'] = T_NUMBER;
_types['8'] = T_NUMBER;
_types['9'] = T_NUMBER;
_types['t'] = T_BOOLEAN;
_types['f'] = T_BOOLEAN;
_types['n'] = T_NULL;
_types['['] = T_ARRAY;
_types['{'] = T_OBJECT;
types = _types;
}

public Config configCache;
private static boolean isStreamingEnabled = false;
final static ValueType[] valueTypes = new ValueType[256];
InputStream in;
byte[] buf;
int head;
Expand All @@ -30,29 +65,6 @@ public class JsonIterator implements Closeable {
char[] reusableChars = new char[32];
Object existingObject = null; // the object should be bind to next

static {
for (int i = 0; i < valueTypes.length; i++) {
valueTypes[i] = ValueType.INVALID;
}
valueTypes['"'] = ValueType.STRING;
valueTypes['-'] = ValueType.NUMBER;
valueTypes['0'] = ValueType.NUMBER;
valueTypes['1'] = ValueType.NUMBER;
valueTypes['2'] = ValueType.NUMBER;
valueTypes['3'] = ValueType.NUMBER;
valueTypes['4'] = ValueType.NUMBER;
valueTypes['5'] = ValueType.NUMBER;
valueTypes['6'] = ValueType.NUMBER;
valueTypes['7'] = ValueType.NUMBER;
valueTypes['8'] = ValueType.NUMBER;
valueTypes['9'] = ValueType.NUMBER;
valueTypes['t'] = ValueType.BOOLEAN;
valueTypes['f'] = ValueType.BOOLEAN;
valueTypes['n'] = ValueType.NULL;
valueTypes['['] = ValueType.ARRAY;
valueTypes['{'] = ValueType.OBJECT;
}

private JsonIterator(InputStream in, byte[] buf, int head, int tail) {
this.in = in;
this.buf = buf;
Expand Down Expand Up @@ -246,13 +258,13 @@ public final BigDecimal readBigDecimal() throws IOException {

public final BigInteger readBigInteger() throws IOException {
// skip whitespace by read next
ValueType valueType = whatIsNext();
if (valueType == ValueType.NULL) {
byte type = whatsNext();
if (type == T_NULL) {
skip();
return null;
}
if (valueType != ValueType.NUMBER) {
throw reportError("readBigDecimal", "not number");
if (type != T_NUMBER) {
throw reportError("readNumber", "not number");
}
IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this);
return new BigInteger(new String(numberChars.chars, 0, numberChars.charsLength));
Expand All @@ -266,31 +278,33 @@ public final Any readAny() throws IOException {
}
}

private final static ReadArrayCallback fillArray = new ReadArrayCallback() {
private static final class ReadArrayObjectCallback implements ReadArrayCallback, ReadObjectCallback {

static final ReadArrayObjectCallback INSTANCE = new ReadArrayObjectCallback();

private ReadArrayObjectCallback() {}

@Override
public boolean handle(JsonIterator iter, Object attachment) throws IOException {
List list = (List) attachment;
list.add(iter.read());
return true;
}
};

private final static ReadObjectCallback fillObject = new ReadObjectCallback() {
@Override
public boolean handle(JsonIterator iter, String field, Object attachment) throws IOException {
Map map = (Map) attachment;
map.put(field, iter.read());
return true;
}
};
}

public final Object read() throws IOException {
try {
ValueType valueType = whatIsNext();
switch (valueType) {
case STRING:
byte type = whatsNext();
switch (type) {
case T_STRING:
return readString();
case NUMBER:
case T_NUMBER:
IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this);
String numberStr = new String(numberChars.chars, 0, numberChars.charsLength);
Double number = Double.valueOf(numberStr);
Expand All @@ -306,21 +320,21 @@ public final Object read() throws IOException {
return longNumber;
}
return number;
case NULL:
case T_NULL:
IterImpl.skipFixedBytes(this, 4);
return null;
case BOOLEAN:
case T_BOOLEAN:
return readBoolean();
case ARRAY:
case T_ARRAY:
ArrayList list = new ArrayList(4);
readArrayCB(fillArray, list);
readArrayCB(ReadArrayObjectCallback.INSTANCE, list);
return list;
case OBJECT:
case T_OBJECT:
Map map = new HashMap(4);
readObjectCB(fillObject, map);
readObjectCB(ReadArrayObjectCallback.INSTANCE, map);
return map;
default:
throw reportError("read", "unexpected value type: " + valueType);
throw reportError("read", "unexpected value type: " + type);
}
} catch (ArrayIndexOutOfBoundsException e) {
throw reportError("read", "premature end");
Expand Down Expand Up @@ -390,11 +404,17 @@ public final Object read(Type type) throws IOException {
}

public ValueType whatIsNext() throws IOException {
ValueType valueType = valueTypes[IterImpl.nextToken(this)];
ValueType valueType = ValueType.ofCharacter(IterImpl.nextToken(this));
unreadByte();
return valueType;
}

private byte whatsNext() throws IOException {
byte type = types[IterImpl.nextToken(this)];
unreadByte();
return type;
}

public void skip() throws IOException {
IterImplSkip.skip(this);
}
Expand Down
32 changes: 31 additions & 1 deletion src/main/java/com/jsoniter/ValueType.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,35 @@ public enum ValueType {
NULL,
BOOLEAN,
ARRAY,
OBJECT
OBJECT;

private static final ValueType[] valueTypes = new ValueType[256];

static {
for (int i = 0; i < valueTypes.length; i++) {
valueTypes[i] = ValueType.INVALID;
}
valueTypes['"'] = ValueType.STRING;
valueTypes['-'] = ValueType.NUMBER;
valueTypes['0'] = ValueType.NUMBER;
valueTypes['1'] = ValueType.NUMBER;
valueTypes['2'] = ValueType.NUMBER;
valueTypes['3'] = ValueType.NUMBER;
valueTypes['4'] = ValueType.NUMBER;
valueTypes['5'] = ValueType.NUMBER;
valueTypes['6'] = ValueType.NUMBER;
valueTypes['7'] = ValueType.NUMBER;
valueTypes['8'] = ValueType.NUMBER;
valueTypes['9'] = ValueType.NUMBER;
valueTypes['t'] = ValueType.BOOLEAN;
valueTypes['f'] = ValueType.BOOLEAN;
valueTypes['n'] = ValueType.NULL;
valueTypes['['] = ValueType.ARRAY;
valueTypes['{'] = ValueType.OBJECT;
}

public static ValueType ofCharacter(byte character) {
return valueTypes[character];
}

}
31 changes: 19 additions & 12 deletions src/main/java/com/jsoniter/extra/Base64.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.jsoniter.output.JsonStream;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

/** A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance
Expand Down Expand Up @@ -76,12 +77,18 @@
*/

abstract class Base64 {
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
static final byte[] BA;
static final int[] IA = new int[256];
private static final byte[] CA;
private static final byte[] BA;
private static final byte[] IA = new byte[256];
static {
Arrays.fill(IA, -1);
for (int i = 0, iS = CA.length; i < iS; i++) {
Arrays.fill(IA, (byte) -1);
try {
CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes("US-ASCII");
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}

for (byte i = 0, iS = (byte) CA.length; i < iS; i++) {
IA[CA[i]] = i;
}
IA['='] = 0;
Expand All @@ -103,10 +110,10 @@ static int encodeToChar(byte[] sArr, char[] dArr, final int start) {
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);

// Encode the int into four chars
dArr[d++] = CA[(i >>> 18) & 0x3f];
dArr[d++] = CA[(i >>> 12) & 0x3f];
dArr[d++] = CA[(i >>> 6) & 0x3f];
dArr[d++] = CA[i & 0x3f];
dArr[d++] = (char) CA[(i >>> 18) & 0x3f];
dArr[d++] = (char) CA[(i >>> 12) & 0x3f];
dArr[d++] = (char) CA[(i >>> 6) & 0x3f];
dArr[d++] = (char) CA[i & 0x3f];
}

// Pad and encode last bits if source isn't even 24 bits.
Expand All @@ -116,9 +123,9 @@ static int encodeToChar(byte[] sArr, char[] dArr, final int start) {
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);

// Set last four chars
dArr[start + dLen - 4] = CA[i >> 12];
dArr[start + dLen - 3] = CA[(i >>> 6) & 0x3f];
dArr[start + dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
dArr[start + dLen - 4] = (char) CA[i >> 12];
dArr[start + dLen - 3] = (char) CA[(i >>> 6) & 0x3f];
dArr[start + dLen - 2] = left == 2 ? (char) CA[i & 0x3f] : '=';
dArr[start + dLen - 1] = '=';
}

Expand Down