hytale-server/com/hypixel/hytale/codec/util/RawJsonReader.java

1476 lines
43 KiB
Java

package com.hypixel.hytale.codec.util;
import ch.randelshofer.fastdoubleparser.JavaDoubleParser;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.ExtraInfo;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.unsafe.UnsafeUtil;
import io.sentry.Sentry;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonDouble;
import org.bson.BsonNull;
import org.bson.BsonString;
import org.bson.BsonValue;
import sun.misc.Unsafe;
public class RawJsonReader implements AutoCloseable {
public static final ThreadLocal<char[]> READ_BUFFER = ThreadLocal.withInitial(() -> new char[131072]);
public static final int DEFAULT_CHAR_BUFFER_SIZE = 32768;
public static final int MIN_CHAR_BUFFER_READ = 16384;
public static final int BUFFER_GROWTH = 1048576;
private static final int UNMARKED = -1;
private int streamIndex;
@Nullable
private Reader in;
@Nullable
private char[] buffer;
private int bufferIndex;
private int bufferSize;
private int markIndex = -1;
private int markLine = -1;
private int markLineStart = -1;
private StringBuilder tempSb;
private int line;
private int lineStart;
public static final int ERROR_LINES_BUFFER = 10;
public RawJsonReader(@Nonnull char[] preFilledBuffer) {
if (preFilledBuffer == null) {
throw new IllegalArgumentException("buffer can't be null!");
} else {
this.in = null;
this.buffer = preFilledBuffer;
this.bufferIndex = 0;
this.streamIndex = 0;
this.bufferSize = preFilledBuffer.length;
}
}
public RawJsonReader(Reader in, @Nonnull char[] buffer) {
if (buffer == null) {
throw new IllegalArgumentException("buffer can't be null!");
} else {
this.in = in;
this.buffer = buffer;
this.bufferIndex = 0;
this.streamIndex = 0;
this.bufferSize = 0;
}
}
public char[] getBuffer() {
return this.buffer;
}
public int getBufferIndex() {
return this.bufferIndex;
}
public int getBufferSize() {
return this.bufferSize;
}
public int getLine() {
return this.line + 1;
}
public int getColumn() {
return this.bufferIndex - this.lineStart + 1;
}
private boolean ensure() throws IOException {
return this.ensure(1);
}
private boolean ensure(int n) throws IOException {
boolean filled;
for (filled = false; this.bufferIndex + n > this.bufferSize; filled = true) {
if (!this.fill()) {
throw this.unexpectedEOF();
}
}
return filled;
}
private boolean fill() throws IOException {
int dst;
int len;
if (this.markIndex <= -1) {
this.streamIndex = this.streamIndex + this.bufferIndex;
dst = 0;
len = this.buffer.length;
} else {
int spaceInBuffer = this.buffer.length - this.bufferIndex;
if (spaceInBuffer > 16384) {
dst = this.bufferIndex;
len = spaceInBuffer;
} else {
int delta = this.bufferIndex - this.markIndex;
if (this.markIndex > 16384) {
System.arraycopy(this.buffer, this.markIndex, this.buffer, 0, delta);
} else {
int newSize = this.bufferIndex + 1048576;
System.err.println("Reallocate: " + this.buffer.length + " to " + newSize);
char[] ncb = new char[newSize];
System.arraycopy(this.buffer, this.markIndex, ncb, 0, delta);
this.buffer = ncb;
}
this.streamIndex = this.streamIndex + this.markIndex;
this.markIndex = 0;
dst = delta;
this.bufferIndex = this.bufferSize = delta;
len = this.buffer.length - delta;
}
}
if (this.in == null) {
return false;
} else {
int n = this.in.read(this.buffer, dst, len);
if (n > 0) {
this.bufferSize = dst + n;
this.bufferIndex = dst;
return true;
} else {
return false;
}
}
}
public int peek() throws IOException {
return this.peek(0);
}
public int peek(int n) throws IOException {
if (this.bufferIndex + n >= this.bufferSize) {
this.fill();
if (this.bufferIndex + n >= this.bufferSize) {
return -1;
}
}
return this.buffer[this.bufferIndex + n];
}
public int read() throws IOException {
if (this.bufferIndex >= this.bufferSize) {
this.fill();
if (this.bufferIndex >= this.bufferSize) {
return -1;
}
}
char c = this.buffer[this.bufferIndex++];
if (c == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
return c;
}
public long skip(long skip) throws IOException {
if (skip < 0L) {
int negativeBufferIndex = -this.bufferIndex;
if (skip < negativeBufferIndex) {
this.bufferIndex = 0;
return negativeBufferIndex;
} else {
this.bufferIndex = (int)(this.bufferIndex + skip);
return skip;
}
} else {
long haveSkipped = 0L;
while (haveSkipped < skip) {
long charsInBuffer = this.bufferSize - this.bufferIndex;
long charsToSkip = skip - haveSkipped;
if (charsToSkip <= charsInBuffer) {
this.bufferIndex = (int)(this.bufferIndex + charsToSkip);
return skip;
}
haveSkipped += charsInBuffer;
this.bufferIndex = this.bufferSize;
this.fill();
if (this.bufferIndex >= this.bufferSize) {
break;
}
}
return haveSkipped;
}
}
public int findOffset(char value) throws IOException {
return this.findOffset(0, value);
}
public int findOffset(int start, char value) throws IOException {
while (true) {
this.ensure();
char c = this.buffer[this.bufferIndex + start];
if (c == value) {
return start;
}
start++;
}
}
public void skipOrThrow(long n) throws IOException {
long skipped = this.skip(n);
if (skipped != n) {
throw new IOException("Failed to skip " + n + " char's!");
}
}
public boolean ready() throws IOException {
return this.buffer != null && this.bufferIndex < this.bufferSize || this.in.ready();
}
public boolean markSupported() {
return true;
}
public void mark(int readAheadLimit) throws IOException {
this.mark();
}
public boolean isMarked() {
return this.markIndex >= 0;
}
public void mark() throws IOException {
if (this.markIndex >= 0) {
throw new IOException("mark can't be used while already marked!");
} else {
this.markIndex = this.bufferIndex;
this.markLine = this.line;
this.markLineStart = this.lineStart;
}
}
public void unmark() {
this.markIndex = -1;
this.markLine = -1;
this.markLineStart = -1;
}
public int getMarkDistance() {
return this.bufferIndex - this.markIndex;
}
public char[] cloneMark() {
return Arrays.copyOfRange(this.buffer, this.markIndex, this.bufferIndex);
}
public void reset() throws IOException {
if (this.markIndex < 0) {
throw new IOException("Stream not marked");
} else {
this.bufferIndex = this.markIndex;
this.markIndex = -1;
this.line = this.markLine;
this.lineStart = this.markLineStart;
this.markLine = -1;
}
}
@Override
public void close() throws IOException {
if (this.buffer != null) {
try {
if (this.in != null) {
this.in.close();
}
} finally {
this.in = null;
this.buffer = null;
}
}
}
public char[] closeAndTakeBuffer() throws IOException {
char[] buffer = this.buffer;
this.close();
return buffer;
}
public boolean peekFor(char consume) throws IOException {
this.ensure();
return this.buffer[this.bufferIndex] == consume;
}
public boolean tryConsume(char consume) throws IOException {
this.ensure();
if (this.buffer[this.bufferIndex] == consume) {
this.bufferIndex++;
if (consume == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
return true;
} else {
return false;
}
}
public boolean tryConsumeString(@Nonnull String str) throws IOException {
this.mark();
if (this.tryConsume('"') && this.tryConsume(str) && this.tryConsume('"')) {
this.unmark();
return true;
} else {
this.reset();
return false;
}
}
public boolean tryConsume(@Nonnull String str) throws IOException {
return this.tryConsume(str, 0);
}
public boolean tryConsume(@Nonnull String str, int start) throws IOException {
while (start < str.length()) {
this.ensure();
while (start < str.length() && this.bufferIndex < this.bufferSize) {
char c = this.buffer[this.bufferIndex];
if (c != str.charAt(start++)) {
return false;
}
this.bufferIndex++;
if (c == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
}
}
return true;
}
public int tryConsumeSome(@Nonnull String str, int start) throws IOException {
while (start < str.length()) {
this.ensure();
while (start < str.length() && this.bufferIndex < this.bufferSize) {
char c = this.buffer[this.bufferIndex];
if (c != str.charAt(start)) {
return start;
}
start++;
this.bufferIndex++;
if (c == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
}
}
return start;
}
public void expect(char expect) throws IOException {
this.ensure();
char read = this.buffer[this.bufferIndex++];
if (read != expect) {
throw this.expecting(read, expect);
} else {
if (expect == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
}
}
public void expect(@Nonnull String str, int start) throws IOException {
this.ensure(str.length() - start);
while (start < str.length()) {
char c = this.buffer[this.bufferIndex];
if (c != str.charAt(start++)) {
throw this.expecting(c, str, start);
}
this.bufferIndex++;
if (c == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
}
}
public boolean tryConsumeOrExpect(char consume, char expect) throws IOException {
this.ensure();
char read = this.buffer[this.bufferIndex];
if (read == consume) {
this.bufferIndex++;
if (consume == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
return true;
} else if (read == expect) {
this.bufferIndex++;
if (expect == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
return false;
} else {
throw this.expecting(read, expect);
}
}
public void consumeWhiteSpace() throws IOException {
while (true) {
if (this.bufferIndex >= this.bufferSize) {
this.fill();
if (this.bufferIndex >= this.bufferSize) {
return;
}
}
while (this.bufferIndex < this.bufferSize) {
char ch = this.buffer[this.bufferIndex];
switch (ch) {
case '\t':
case '\r':
case ' ':
this.bufferIndex++;
break;
case '\n':
this.bufferIndex++;
this.line++;
this.lineStart = this.bufferIndex;
break;
default:
if (!Character.isWhitespace(ch)) {
return;
}
this.bufferIndex++;
}
}
}
}
public void consumeIgnoreCase(@Nonnull String str, int start) throws IOException {
this.ensure(str.length() - start);
while (start < str.length()) {
char c = this.buffer[this.bufferIndex];
if (!equalsIgnoreCase(c, str.charAt(start++))) {
throw this.expecting(c, str, start);
}
this.bufferIndex++;
if (c == '\n') {
this.line++;
this.lineStart = this.bufferIndex;
}
}
}
@Nonnull
public String readString() throws IOException {
this.expect('"');
return this.readRemainingString();
}
@Nonnull
public String readRemainingString() throws IOException {
if (this.tempSb == null) {
this.tempSb = new StringBuilder(1024);
}
while (true) {
this.ensure();
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex++];
switch (read) {
case '"':
String string = this.tempSb.toString();
this.tempSb.setLength(0);
return string;
case '\\':
this.ensure();
read = this.buffer[this.bufferIndex++];
switch (read) {
case '"':
case '/':
case '\\':
this.tempSb.append(read);
continue;
case 'b':
this.tempSb.append('\b');
continue;
case 'f':
this.tempSb.append('\f');
continue;
case 'n':
this.tempSb.append('\n');
continue;
case 'r':
this.tempSb.append('\r');
continue;
case 't':
this.tempSb.append('\t');
continue;
case 'u':
this.ensure(4);
read = this.buffer[this.bufferIndex++];
int digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "reading string");
}
int hex = digit << 12;
read = this.buffer[this.bufferIndex++];
digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "reading string");
}
hex |= digit << 8;
read = this.buffer[this.bufferIndex++];
digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "reading string");
}
hex |= digit << 4;
read = this.buffer[this.bufferIndex++];
digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "reading string");
}
hex |= digit;
this.tempSb.appendCodePoint(hex);
continue;
default:
throw this.expecting(read, "escape char");
}
default:
if (Character.isISOControl(read)) {
throw this.unexpectedChar(read);
}
this.tempSb.append(read);
}
}
}
}
public void skipString() throws IOException {
this.expect('"');
this.skipRemainingString();
}
public void skipRemainingString() throws IOException {
while (true) {
this.ensure();
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex++];
switch (read) {
case '"':
return;
case '\\':
this.ensure();
read = this.buffer[this.bufferIndex++];
switch (read) {
case '"':
case '/':
case '\\':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
continue;
case 'u':
this.ensure(4);
read = this.buffer[this.bufferIndex++];
int digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "skipping string");
}
read = this.buffer[this.bufferIndex++];
digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "skipping string");
}
read = this.buffer[this.bufferIndex++];
digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "skipping string");
}
read = this.buffer[this.bufferIndex++];
digit = Character.digit(read, 16);
if (digit == -1) {
throw this.expectingWhile(read, "HEX Digit 0-F", "skipping string");
}
continue;
default:
throw this.expecting(read, "escape char");
}
default:
if (Character.isISOControl(read)) {
throw this.unexpectedChar(read);
}
}
}
}
}
public long readStringPartAsLong(int count) throws IOException {
assert count > 0 && count <= 4;
return UnsafeUtil.UNSAFE != null && this.bufferIndex + count <= this.bufferSize
? this.readStringPartAsLongUnsafe(count)
: this.readStringPartAsLongSlow(count);
}
protected long readStringPartAsLongSlow(int count) throws IOException {
this.ensure(count);
char c1 = this.buffer[this.bufferIndex++];
if (count == 1) {
return c1;
} else {
char c2 = this.buffer[this.bufferIndex++];
long value = c1 | (long)c2 << 16;
if (count == 2) {
return value;
} else {
char c3 = this.buffer[this.bufferIndex++];
value |= (long)c3 << 32;
if (count == 3) {
return value;
} else {
char c4 = this.buffer[this.bufferIndex++];
return value | (long)c4 << 48;
}
}
}
}
protected long readStringPartAsLongUnsafe(int count) throws IOException {
this.ensure(count);
int offset = Unsafe.ARRAY_CHAR_BASE_OFFSET + Unsafe.ARRAY_CHAR_INDEX_SCALE * this.bufferIndex;
long value = UnsafeUtil.UNSAFE.getLong(this.buffer, offset);
this.bufferIndex += count;
long mask = count == 4 ? -1L : (1L << count * 16) - 1L;
return value & mask;
}
public boolean readBooleanValue() throws IOException {
this.ensure(4);
char read = this.buffer[this.bufferIndex++];
return switch (read) {
case 'F', 'f' -> {
this.consumeIgnoreCase("false", 1);
yield false;
}
case 'T', 't' -> {
this.consumeIgnoreCase("true", 1);
yield true;
}
default -> throw this.expecting(read, "true' or 'false");
};
}
public void skipBooleanValue() throws IOException {
this.readBooleanValue();
}
@Nullable
public Void readNullValue() throws IOException {
this.consumeIgnoreCase("null", 0);
return null;
}
public void skipNullValue() throws IOException {
this.consumeIgnoreCase("null", 0);
}
public double readDoubleValue() throws IOException {
int start = this.bufferIndex;
while (true) {
if (this.bufferIndex >= this.bufferSize) {
this.fill();
if (this.bufferIndex >= this.bufferSize) {
return JavaDoubleParser.parseDouble(this.buffer, start, this.bufferIndex - start);
}
}
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex];
switch (read) {
case '+':
case '-':
case '.':
case 'E':
case 'e':
this.bufferIndex++;
break;
default:
if (!Character.isDigit(read)) {
return JavaDoubleParser.parseDouble(this.buffer, start, this.bufferIndex - start);
}
this.bufferIndex++;
}
}
}
}
public void skipDoubleValue() throws IOException {
while (true) {
if (this.bufferIndex >= this.bufferSize) {
this.fill();
if (this.bufferIndex >= this.bufferSize) {
return;
}
}
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex];
switch (read) {
case '+':
case '-':
case '.':
case 'E':
case 'e':
this.bufferIndex++;
break;
default:
if (!Character.isDigit(read)) {
return;
}
this.bufferIndex++;
}
}
}
}
public float readFloatValue() throws IOException {
return (float)this.readDoubleValue();
}
public void skipFloatValue() throws IOException {
this.skipDoubleValue();
}
public long readLongValue() throws IOException {
return this.readLongValue(10);
}
public long readLongValue(int radix) throws IOException {
if (this.tempSb == null) {
this.tempSb = new StringBuilder(1024);
}
while (true) {
if (this.bufferIndex >= this.bufferSize) {
this.fill();
if (this.bufferIndex >= this.bufferSize) {
long value = Long.parseLong(this.tempSb, 0, this.tempSb.length(), radix);
this.tempSb.setLength(0);
return value;
}
}
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex];
switch (read) {
case '+':
case '-':
case '.':
case 'E':
case 'e':
this.tempSb.append(read);
this.bufferIndex++;
break;
default:
if (Character.digit(read, radix) < 0) {
long value = Long.parseLong(this.tempSb, 0, this.tempSb.length(), radix);
this.tempSb.setLength(0);
return value;
}
this.tempSb.append(read);
this.bufferIndex++;
}
}
}
}
public void skipLongValue() throws IOException {
this.skipLongValue(10);
}
public void skipLongValue(int radix) throws IOException {
while (true) {
if (this.bufferIndex >= this.bufferSize) {
this.fill();
if (this.bufferIndex >= this.bufferSize) {
return;
}
}
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex];
switch (read) {
case '+':
case '-':
case '.':
case 'E':
case 'e':
this.bufferIndex++;
break;
default:
if (Character.digit(read, radix) < 0) {
return;
}
this.bufferIndex++;
}
}
}
}
public int readIntValue() throws IOException {
return this.readIntValue(10);
}
public int readIntValue(int radix) throws IOException {
return (int)this.readLongValue(radix);
}
public byte readByteValue() throws IOException {
return this.readByteValue(10);
}
public byte readByteValue(int radix) throws IOException {
return (byte)this.readLongValue(radix);
}
public void skipIntValue() throws IOException {
this.skipLongValue();
}
public void skipIntValue(int radix) throws IOException {
this.skipLongValue(radix);
}
public void skipObject() throws IOException {
this.expect('{');
this.skipObjectContinued();
}
public void skipObjectContinued() throws IOException {
int count = 1;
while (true) {
this.ensure();
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex++];
switch (read) {
case '\n':
this.line++;
this.lineStart = this.bufferIndex;
break;
case '{':
count++;
break;
case '}':
if (--count == 0) {
return;
}
}
}
}
}
public void skipArray() throws IOException {
this.expect('[');
this.skipArrayContinued();
}
public void skipArrayContinued() throws IOException {
int count = 1;
while (true) {
this.ensure();
while (this.bufferIndex < this.bufferSize) {
char read = this.buffer[this.bufferIndex++];
switch (read) {
case '\n':
this.line++;
this.lineStart = this.bufferIndex;
break;
case '[':
count++;
break;
case ']':
if (--count == 0) {
return;
}
}
}
}
}
public void skipValue() throws IOException {
this.ensure();
char read = this.buffer[this.bufferIndex];
switch (read) {
case '"':
this.skipString();
break;
case '+':
case '-':
this.skipDoubleValue();
break;
case 'F':
case 'f':
this.consumeIgnoreCase("false", 0);
break;
case 'N':
case 'n':
this.skipNullValue();
break;
case 'T':
case 't':
this.consumeIgnoreCase("true", 0);
break;
case '[':
this.skipArray();
break;
case '{':
this.skipObject();
break;
default:
if (!Character.isDigit(read)) {
throw this.unexpectedChar(read);
}
this.skipDoubleValue();
}
}
@Nonnull
private IOException unexpectedEOF() {
return new IOException("Unexpected EOF!");
}
@Nonnull
private IOException unexpectedChar(char read) {
return new IOException("Unexpected character: " + Integer.toHexString(read) + ", '" + read + "'!");
}
@Nonnull
private IOException expecting(char read, char expect) {
return new IOException("Unexpected character: " + Integer.toHexString(read) + ", '" + read + "' expected '" + expect + "'!");
}
@Nonnull
private IOException expecting(char read, String expected) {
return new IOException("Unexpected character: " + Integer.toHexString(read) + ", '" + read + "' expected '" + expected + "'!");
}
@Nonnull
private IOException expectingWhile(char read, String expected, String reason) {
return new IOException("Unexpected character: " + Integer.toHexString(read) + ", '" + read + "' expected '" + expected + "' while " + reason + "!");
}
@Nonnull
private IOException expecting(char read, @Nonnull String expected, int index) {
return new IOException(
"Unexpected character: "
+ Integer.toHexString(read)
+ ", '"
+ read
+ "' when consuming string '"
+ expected
+ "' expected '"
+ expected.substring(index - 1)
+ "'!"
);
}
@Nonnull
@Override
public String toString() {
if (this.buffer == null) {
return "Closed RawJsonReader";
} else {
StringBuilder s = new StringBuilder("Index: ")
.append(this.streamIndex + this.bufferIndex)
.append(", StreamIndex: ")
.append(this.streamIndex)
.append(", BufferIndex: ")
.append(this.bufferIndex)
.append(", BufferSize: ")
.append(this.bufferSize)
.append(", Line: ")
.append(this.line)
.append(", MarkIndex: ")
.append(this.markIndex)
.append(", MarkLine: ")
.append(this.markLine)
.append('\n');
int lineStart = this.findLineStart(this.bufferIndex);
int lineNumber;
for (lineNumber = this.line; lineStart > 0 && lineNumber > this.line - 10; lineNumber--) {
lineStart = this.findLineStart(lineStart);
}
while (lineNumber < this.line) {
lineStart = this.appendLine(s, lineStart, lineNumber);
lineNumber++;
}
for (int var4 = this.appendProblemLine(s, lineStart, this.line); var4 < this.bufferSize && lineNumber < this.line + 10; lineNumber++) {
var4 = this.appendLine(s, var4, lineNumber);
}
return this.in == null ? "Buffer RawJsonReader: " + s : "Streamed RawJsonReader: " + s;
}
}
private int findLineStart(int index) {
index--;
while (index > 0 && this.buffer[index] != '\n') {
index--;
}
return index;
}
private int appendLine(@Nonnull StringBuilder sb, int index, int lineNumber) {
int lineStart = index + 1;
index++;
while (index < this.bufferSize && this.buffer[index] != '\n') {
index++;
}
sb.append("L").append(String.format("%3s", lineNumber)).append('|').append(this.buffer, lineStart, index - lineStart).append('\n');
return index;
}
private int appendProblemLine(@Nonnull StringBuilder sb, int index, int lineNumber) {
int lineStart = ++index;
while (index < this.bufferSize && this.buffer[index] != '\n') {
index++;
}
sb.append("L").append(String.format("%3s", lineNumber)).append('>').append(this.buffer, lineStart, index - lineStart).append('\n');
sb.append(" |");
sb.append("-".repeat(Math.max(0, this.bufferIndex - lineStart - 1)));
sb.append('^').append('\n');
return index;
}
@Nonnull
public static RawJsonReader fromRawString(String str) {
return fromJsonString("\"" + str + "\"");
}
@Nonnull
public static RawJsonReader fromJsonString(@Nonnull String str) {
return fromBuffer(str.toCharArray());
}
@Nonnull
public static RawJsonReader fromPath(@Nonnull Path path, @Nonnull char[] buffer) throws IOException {
return new RawJsonReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8), buffer);
}
@Nonnull
public static RawJsonReader fromBuffer(@Nonnull char[] buffer) {
return new RawJsonReader(buffer);
}
public static boolean equalsIgnoreCase(char c1, char c2) {
if (c1 == c2) {
return true;
} else {
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
return u1 == u2 || Character.toLowerCase(u1) == Character.toLowerCase(u2);
}
}
@Deprecated
public static BsonDocument readBsonDocument(@Nonnull RawJsonReader reader) throws IOException {
reader.expect('{');
StringBuilder sb = new StringBuilder("{");
readBsonDocument0(reader, sb);
return BsonDocument.parse(sb.toString());
}
private static void readBsonDocument0(@Nonnull RawJsonReader reader, @Nonnull StringBuilder sb) throws IOException {
int count = 1;
int read;
while ((read = reader.read()) != -1) {
sb.append((char)read);
switch (read) {
case 10:
reader.line++;
reader.lineStart = reader.bufferIndex;
break;
case 91:
readBsonArray0(reader, sb);
break;
case 123:
count++;
break;
case 125:
if (--count == 0) {
return;
}
}
}
throw reader.unexpectedEOF();
}
@Deprecated
public static BsonArray readBsonArray(@Nonnull RawJsonReader reader) throws IOException {
reader.expect('[');
StringBuilder sb = new StringBuilder("[");
readBsonArray0(reader, sb);
return BsonArray.parse(sb.toString());
}
private static void readBsonArray0(@Nonnull RawJsonReader reader, @Nonnull StringBuilder sb) throws IOException {
int count = 1;
int read;
while ((read = reader.read()) != -1) {
sb.append((char)read);
switch (read) {
case 10:
reader.line++;
reader.lineStart = reader.bufferIndex;
break;
case 91:
count++;
break;
case 93:
if (--count == 0) {
return;
}
break;
case 123:
readBsonDocument0(reader, sb);
}
}
throw reader.unexpectedEOF();
}
@Deprecated
public static BsonValue readBsonValue(@Nonnull RawJsonReader reader) throws IOException {
int read = reader.peek();
if (read == -1) {
throw reader.unexpectedEOF();
} else {
return (BsonValue)(switch (read) {
case 34 -> new BsonString(reader.readString());
case 43, 45 -> new BsonDouble(reader.readDoubleValue());
case 70, 84, 102, 116 -> reader.readBooleanValue() ? BsonBoolean.TRUE : BsonBoolean.FALSE;
case 78, 110 -> {
reader.skipNullValue();
yield BsonNull.VALUE;
}
case 91 -> readBsonArray(reader);
case 123 -> readBsonDocument(reader);
default -> {
if (!Character.isDigit(read)) {
throw reader.unexpectedChar((char)read);
}
yield new BsonDouble(reader.readDoubleValue());
}
});
}
}
public static boolean seekToKey(@Nonnull RawJsonReader reader, @Nonnull String search) throws IOException {
reader.consumeWhiteSpace();
reader.expect('{');
reader.consumeWhiteSpace();
if (reader.tryConsume('}')) {
return false;
} else {
while (true) {
reader.expect('"');
if (reader.tryConsume(search) && reader.tryConsume('"')) {
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
return true;
}
reader.skipRemainingString();
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
reader.skipValue();
reader.consumeWhiteSpace();
if (reader.tryConsumeOrExpect('}', ',')) {
return false;
}
reader.consumeWhiteSpace();
}
}
}
@Nullable
public static String seekToKeyFromObjectStart(@Nonnull RawJsonReader reader, @Nonnull String search1, @Nonnull String search2) throws IOException {
reader.consumeWhiteSpace();
reader.expect('{');
reader.consumeWhiteSpace();
if (reader.tryConsume('}')) {
return null;
} else {
while (true) {
reader.expect('"');
int search1Index = reader.tryConsumeSome(search1, 0);
if (search1Index == search1.length() && reader.tryConsume('"')) {
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
return search1;
}
if (reader.tryConsume(search2, search1Index) && reader.tryConsume('"')) {
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
return search2;
}
reader.skipRemainingString();
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
reader.skipValue();
reader.consumeWhiteSpace();
if (reader.tryConsumeOrExpect('}', ',')) {
return null;
}
reader.consumeWhiteSpace();
}
}
}
@Nullable
public static String seekToKeyFromObjectContinued(@Nonnull RawJsonReader reader, @Nonnull String search1, @Nonnull String search2) throws IOException {
reader.consumeWhiteSpace();
if (reader.tryConsumeOrExpect('}', ',')) {
return null;
} else {
reader.consumeWhiteSpace();
while (true) {
reader.expect('"');
int search1Index = reader.tryConsumeSome(search1, 0);
if (search1Index == search1.length() && reader.tryConsume('"')) {
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
return search1;
}
if (reader.tryConsume(search2, search1Index) && reader.tryConsume('"')) {
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
return search2;
}
reader.skipRemainingString();
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
reader.skipValue();
reader.consumeWhiteSpace();
if (reader.tryConsumeOrExpect('}', ',')) {
return null;
}
reader.consumeWhiteSpace();
}
}
}
public static void validateBsonDocument(@Nonnull RawJsonReader reader) throws IOException {
reader.expect('{');
reader.consumeWhiteSpace();
if (!reader.tryConsume('}')) {
while (true) {
reader.skipString();
reader.consumeWhiteSpace();
reader.expect(':');
reader.consumeWhiteSpace();
validateBsonValue(reader);
reader.consumeWhiteSpace();
if (reader.tryConsumeOrExpect('}', ',')) {
return;
}
reader.consumeWhiteSpace();
}
}
}
public static void validateBsonArray(@Nonnull RawJsonReader reader) throws IOException {
reader.expect('[');
reader.consumeWhiteSpace();
if (!reader.tryConsume(']')) {
while (true) {
validateBsonValue(reader);
reader.consumeWhiteSpace();
if (reader.tryConsumeOrExpect(']', ',')) {
return;
}
reader.consumeWhiteSpace();
}
}
}
public static void validateBsonValue(@Nonnull RawJsonReader reader) throws IOException {
int read = reader.peek();
if (read == -1) {
throw reader.unexpectedEOF();
} else {
switch (read) {
case 34:
reader.skipString();
break;
case 43:
case 45:
reader.readDoubleValue();
break;
case 70:
case 84:
case 102:
case 116:
reader.readBooleanValue();
break;
case 78:
case 110:
reader.skipNullValue();
break;
case 91:
validateBsonArray(reader);
break;
case 123:
validateBsonDocument(reader);
break;
default:
if (Character.isDigit(read)) {
reader.readDoubleValue();
return;
}
throw reader.unexpectedChar((char)read);
}
}
}
@Nullable
public static <T> T readSync(@Nonnull Path path, @Nonnull Codec<T> codec, @Nonnull HytaleLogger logger) throws IOException {
char[] buffer = READ_BUFFER.get();
RawJsonReader reader = fromPath(path, buffer);
Object var7;
try {
ExtraInfo extraInfo = ExtraInfo.THREAD_LOCAL.get();
T value = codec.decodeJson(reader, extraInfo);
extraInfo.getValidationResults().logOrThrowValidatorExceptions(logger);
var7 = value;
} finally {
char[] newBuffer = reader.closeAndTakeBuffer();
if (newBuffer.length > buffer.length) {
READ_BUFFER.set(newBuffer);
}
}
return (T)var7;
}
@Nullable
public static <T> T readSyncWithBak(@Nonnull Path path, @Nonnull Codec<T> codec, @Nonnull HytaleLogger logger) {
try {
return readSync(path, codec, logger);
} catch (IOException var8) {
Path backupPath = path.resolveSibling(path.getFileName() + ".bak");
if (var8 instanceof NoSuchFileException && !Files.exists(backupPath)) {
return null;
} else {
if (Sentry.isEnabled()) {
Sentry.captureException(var8);
}
((HytaleLogger.Api)logger.at(Level.SEVERE).withCause(var8)).log("Failed to load from primary file %s, trying backup file", path);
try {
T value = readSync(backupPath, codec, logger);
logger.at(Level.WARNING).log("Loaded from backup file %s after primary file %s failed to load", backupPath, path);
return value;
} catch (NoSuchFileException var6) {
return null;
} catch (IOException var7) {
((HytaleLogger.Api)logger.at(Level.WARNING).withCause(var8)).log("Failed to load from both %s and backup file %s", path, backupPath);
return null;
}
}
}
}
}