Skip to content

Commit 52ac6e3

Browse files
committed
Fix CharsJsonIterator::parseNumber
1 parent e70139d commit 52ac6e3

File tree

9 files changed

+137
-49
lines changed

9 files changed

+137
-49
lines changed

gradle/wrapper/gradle-wrapper.jar

-19.8 KB
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

gradlew

+7-7
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,15 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145145
case $MAX_FD in #(
146146
max*)
147147
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148-
# shellcheck disable=SC3045
148+
# shellcheck disable=SC2039,SC3045
149149
MAX_FD=$( ulimit -H -n ) ||
150150
warn "Could not query maximum file descriptor limit"
151151
esac
152152
case $MAX_FD in #(
153153
'' | soft) :;; #(
154154
*)
155155
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156-
# shellcheck disable=SC3045
156+
# shellcheck disable=SC2039,SC3045
157157
ulimit -n "$MAX_FD" ||
158158
warn "Could not set maximum file descriptor limit to $MAX_FD"
159159
esac
@@ -202,11 +202,11 @@ fi
202202
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203203
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204204

205-
# Collect all arguments for the java command;
206-
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
207-
# shell script including quotes and variable substitutions, so put them in
208-
# double quotes to make sure that they get re-expanded; and
209-
# * put everything else in single quotes, so that it's not re-expanded.
205+
# Collect all arguments for the java command:
206+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207+
# and any embedded shellness will be escaped.
208+
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209+
# treated as '${Hostname}' itself on the command line.
210210

211211
set -- \
212212
"-Dorg.gradle.appname=$APP_BASE_NAME" \

gradlew.bat

+10-10
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
4343
%JAVA_EXE% -version >NUL 2>&1
4444
if %ERRORLEVEL% equ 0 goto execute
4545

46-
echo.
47-
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48-
echo.
49-
echo Please set the JAVA_HOME variable in your environment to match the
50-
echo location of your Java installation.
46+
echo. 1>&2
47+
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48+
echo. 1>&2
49+
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50+
echo location of your Java installation. 1>&2
5151

5252
goto fail
5353

@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
5757

5858
if exist "%JAVA_EXE%" goto execute
5959

60-
echo.
61-
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62-
echo.
63-
echo Please set the JAVA_HOME variable in your environment to match the
64-
echo location of your Java installation.
60+
echo. 1>&2
61+
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62+
echo. 1>&2
63+
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64+
echo location of your Java installation. 1>&2
6565

6666
goto fail
6767

systems.comodal.json_iterator/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ publishing {
128128
name = "GitHubPackages"
129129
url = "https://maven.pkg.github.com/comodal/json-iterator"
130130
credentials {
131-
username = System.getenv("GITHUB_ACTOR")
132-
password = System.getenv("GITHUB_TOKEN")
131+
username = System.getenv("GITHUB_ACTOR") ?: project.findProperty("gpr.user.write")
132+
password = System.getenv("GITHUB_TOKEN") ?: project.findProperty("gpr.token.write")
133133
}
134134
}
135135
}

systems.comodal.json_iterator/src/main/java/systems/comodal/jsoniter/CharsJsonIterator.java

+24-24
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ void skipPastEndQuote() {
158158
}
159159

160160
@Override
161-
final int parseNumber() {
161+
int parseNumber() {
162162
for (int i = head, len = 0; ; i++) {
163163
if (i == tail) {
164164
head = tail;
@@ -206,7 +206,7 @@ private char[] handleEscapes(final int from, final int len) {
206206
}
207207

208208
@Override
209-
final <R> R parse(final CharBufferFunction<R> applyChars) {
209+
<R> R parse(final CharBufferFunction<R> applyChars) {
210210
final int from = head;
211211
final int len = parse(from);
212212
if (numEscapes > 0) {
@@ -218,7 +218,7 @@ final <R> R parse(final CharBufferFunction<R> applyChars) {
218218
}
219219

220220
@Override
221-
final <C, R> R parse(final C context, final ContextCharBufferFunction<C, R> applyChars) {
221+
<C, R> R parse(final C context, final ContextCharBufferFunction<C, R> applyChars) {
222222
final int from = head;
223223
final int len = parse(from);
224224
if (numEscapes > 0) {
@@ -278,7 +278,7 @@ <C> long parse(final C context, final ContextCharBufferToLongFunction<C> applyCh
278278
}
279279

280280
@Override
281-
final boolean parse(final CharBufferPredicate testChars) {
281+
boolean parse(final CharBufferPredicate testChars) {
282282
final int from = head;
283283
final int len = parse(from);
284284
if (numEscapes > 0) {
@@ -290,7 +290,7 @@ final boolean parse(final CharBufferPredicate testChars) {
290290
}
291291

292292
@Override
293-
final <C> boolean parse(final C context, final ContextCharBufferPredicate<C> testChars) {
293+
<C> boolean parse(final C context, final ContextCharBufferPredicate<C> testChars) {
294294
final int from = head;
295295
final int len = parse(from);
296296
if (numEscapes > 0) {
@@ -302,7 +302,7 @@ final <C> boolean parse(final C context, final ContextCharBufferPredicate<C> tes
302302
}
303303

304304
@Override
305-
final void parse(final CharBufferConsumer testChars) {
305+
void parse(final CharBufferConsumer testChars) {
306306
final int from = head;
307307
final int len = parse(from);
308308
if (numEscapes > 0) {
@@ -314,7 +314,7 @@ final void parse(final CharBufferConsumer testChars) {
314314
}
315315

316316
@Override
317-
final <C> void parse(final C context, final ContextCharBufferConsumer<C> testChars) {
317+
<C> void parse(final C context, final ContextCharBufferConsumer<C> testChars) {
318318
final int from = head;
319319
final int len = parse(from);
320320
if (numEscapes > 0) {
@@ -326,12 +326,12 @@ final <C> void parse(final C context, final ContextCharBufferConsumer<C> testCha
326326
}
327327

328328
@Override
329-
final boolean fieldEquals(final String field, final int offset, final int len) {
329+
boolean fieldEquals(final String field, final int offset, final int len) {
330330
return JsonIterator.fieldEquals(field, buf, offset, len);
331331
}
332332

333333
@Override
334-
final boolean breakOut(final FieldBufferPredicate fieldBufferFunction, final int offset, final int len) {
334+
boolean breakOut(final FieldBufferPredicate fieldBufferFunction, final int offset, final int len) {
335335
if (numEscapes > 0) {
336336
final char[] chars = handleEscapes(offset, len);
337337
return !fieldBufferFunction.test(chars, 0, chars.length, this);
@@ -341,7 +341,7 @@ final boolean breakOut(final FieldBufferPredicate fieldBufferFunction, final int
341341
}
342342

343343
@Override
344-
final <C> boolean breakOut(final C context, final ContextFieldBufferPredicate<C> fieldBufferFunction, final int offset, final int len) {
344+
<C> boolean breakOut(final C context, final ContextFieldBufferPredicate<C> fieldBufferFunction, final int offset, final int len) {
345345
if (numEscapes > 0) {
346346
final char[] chars = handleEscapes(offset, len);
347347
return !fieldBufferFunction.test(context, chars, 0, chars.length, this);
@@ -361,7 +361,7 @@ <C> long test(final C context, final long mask, final ContextFieldBufferMaskedPr
361361
}
362362

363363
@Override
364-
final <R> R apply(final FieldBufferFunction<R> fieldBufferFunction, final int offset, final int len) {
364+
<R> R apply(final FieldBufferFunction<R> fieldBufferFunction, final int offset, final int len) {
365365
if (numEscapes > 0) {
366366
final char[] chars = handleEscapes(offset, len);
367367
return fieldBufferFunction.apply(chars, 0, chars.length, this);
@@ -371,7 +371,7 @@ final <R> R apply(final FieldBufferFunction<R> fieldBufferFunction, final int of
371371
}
372372

373373
@Override
374-
final <C, R> R apply(final C context, final ContextFieldBufferFunction<C, R> fieldBufferFunction, final int offset, final int len) {
374+
<C, R> R apply(final C context, final ContextFieldBufferFunction<C, R> fieldBufferFunction, final int offset, final int len) {
375375
if (numEscapes > 0) {
376376
final char[] chars = handleEscapes(offset, len);
377377
return fieldBufferFunction.apply(context, chars, 0, chars.length, this);
@@ -381,45 +381,45 @@ final <C, R> R apply(final C context, final ContextFieldBufferFunction<C, R> fie
381381
}
382382

383383
@Override
384-
final BigDecimal parseBigDecimal(final CharBufferFunction<BigDecimal> parseChars) {
384+
BigDecimal parseBigDecimal(final CharBufferFunction<BigDecimal> parseChars) {
385385
final int len = parseNumber();
386386
return parseChars.apply(buf, head - len, len);
387387
}
388388

389389
@Override
390-
final String parsedNumberAsString(final int len) {
390+
String parsedNumberAsString(final int len) {
391391
return new String(buf, head - len, len);
392392
}
393393

394394
@Override
395-
final <R> R parseNumber(final CharBufferFunction<R> applyChars, final int len) {
396-
return applyChars.apply(buf, 0, len);
395+
<R> R parseNumber(final CharBufferFunction<R> applyChars, final int len) {
396+
return applyChars.apply(buf, head - len, len);
397397
}
398398

399399
@Override
400-
final <C, R> R parseNumber(final C context,
401-
final ContextCharBufferFunction<C, R> applyChars,
402-
final int len) {
403-
return applyChars.apply(context, buf, 0, len);
400+
<C, R> R parseNumber(final C context,
401+
final ContextCharBufferFunction<C, R> applyChars,
402+
final int len) {
403+
return applyChars.apply(context, buf, head - len, len);
404404
}
405405

406406
@Override
407407
int parseNumber(final CharBufferToIntFunction applyChars, final int len) {
408-
return applyChars.applyAsInt(buf, 0, len);
408+
return applyChars.applyAsInt(buf, head - len, len);
409409
}
410410

411411
@Override
412412
<C> int parseNumber(final C context, final ContextCharBufferToIntFunction<C> applyChars, final int len) {
413-
return applyChars.applyAsInt(context, buf, 0, len);
413+
return applyChars.applyAsInt(context, buf, head - len, len);
414414
}
415415

416416
@Override
417417
long parseNumber(final CharBufferToLongFunction applyChars, final int len) {
418-
return applyChars.applyAsLong(buf, 0, len);
418+
return applyChars.applyAsLong(buf, head - len, len);
419419
}
420420

421421
@Override
422422
<C> long parseNumber(final C context, final ContextCharBufferToLongFunction<C> applyChars, final int len) {
423-
return applyChars.applyAsLong(context, buf, 0, len);
423+
return applyChars.applyAsLong(context, buf, head - len, len);
424424
}
425425
}

systems.comodal.json_iterator/src/main/java/systems/comodal/jsoniter/JIUtil.java

+53-1
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,63 @@ public static long compileReplacePattern(final byte byteToFind) {
3939
| (pattern << 56);
4040
}
4141

42+
43+
public static String escapeQuotesChecked(final String str) {
44+
final int len = str.length();
45+
int from = 0;
46+
do {
47+
from = str.indexOf('"', from);
48+
if (from < 0) {
49+
return str;
50+
}
51+
int i = from - 1;
52+
if (i < 0) {
53+
return escapeQuotes(str, from);
54+
}
55+
if (str.charAt(i) == '\\') {
56+
int escapes = 1;
57+
while (--i >= 0) {
58+
if (str.charAt(i) == '\\') {
59+
++escapes;
60+
} else {
61+
break;
62+
}
63+
}
64+
if ((escapes & 1) == 0) {
65+
return escapeQuotes(str, from);
66+
}
67+
} else {
68+
return escapeQuotes(str, from);
69+
}
70+
} while (++from < len);
71+
return str;
72+
}
73+
4274
public static String escapeQuotes(final String str) {
75+
return escapeQuotes(str, -1);
76+
}
77+
78+
private static String escapeQuotes(final String str, final int firstUnescapedQuote) {
4379
final char[] chars = str.toCharArray();
4480
final char[] escaped = new char[chars.length << 1];
81+
82+
int from, to;
83+
if (firstUnescapedQuote < 0) {
84+
from = 0;
85+
to = 0;
86+
} else if (firstUnescapedQuote > 0) {
87+
System.arraycopy(chars, 0, escaped, 0, firstUnescapedQuote);
88+
escaped[firstUnescapedQuote] = '\\';
89+
from = firstUnescapedQuote;
90+
to = firstUnescapedQuote + 1;
91+
} else {
92+
escaped[0] = '\\';
93+
from = 0;
94+
to = 1;
95+
}
96+
4597
char c;
46-
for (int escapes = 0, from = 0, dest = 0, to = 0; ; to++) {
98+
for (int escapes = 0, dest = to; ; ++to) {
4799
if (to == chars.length) {
48100
if (from == 0) {
49101
return str;

systems.comodal.json_iterator/src/test/java/systems/comodal/jsoniter/TestInteger.java

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import org.junit.jupiter.params.provider.MethodSource;
55
import systems.comodal.jsoniter.factories.JsonIteratorFactory;
66

7+
import java.math.RoundingMode;
8+
import java.time.Instant;
9+
10+
import static java.util.concurrent.TimeUnit.MICROSECONDS;
711
import static org.junit.jupiter.api.Assertions.assertEquals;
812
import static org.junit.jupiter.api.Assertions.assertThrows;
913

@@ -197,4 +201,30 @@ void test_max_int(final JsonIteratorFactory factory) {
197201
ji.readArray();
198202
assertEquals(Integer.MIN_VALUE, ji.readInt());
199203
}
204+
205+
@ParameterizedTest
206+
@MethodSource("systems.comodal.jsoniter.TestFactories#factories")
207+
void testParseMicroseconds(final JsonIteratorFactory factory) {
208+
final CharBufferFunction<Instant> MICROS_PARSER = (buf, offset, len) -> {
209+
final int secondsLength = len - 6;
210+
final long seconds = Long.parseLong(new String(buf, offset, secondsLength));
211+
final long micros = Long.parseLong(new String(buf, offset + secondsLength, 6));
212+
return Instant.ofEpochSecond(seconds, MICROSECONDS.toNanos(micros));
213+
};
214+
215+
final var json = "{\"timestamp\": 1694687692989999}";
216+
var ji = factory.create(json);
217+
ji.skipObjField();
218+
var instant = ji.applyNumberChars(MICROS_PARSER);
219+
final var expected = Instant.ofEpochSecond(1694687692, 989999000);
220+
assertEquals(expected, instant);
221+
222+
ji = factory.create(json);
223+
ji.skipObjField();
224+
final var micros = ji.readBigDecimal().movePointLeft(6);
225+
final var seconds = micros.setScale(0, RoundingMode.DOWN);
226+
final var nanos = micros.subtract(seconds).movePointRight(9);
227+
instant = Instant.ofEpochSecond(seconds.longValue(), nanos.longValue());
228+
assertEquals(expected, instant);
229+
}
200230
}

systems.comodal.json_iterator/src/test/java/systems/comodal/jsoniter/TestString.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,21 @@ void test_ascii_string(final JsonIteratorFactory factory) {
3333

3434
@Test
3535
void testEscapeQuotes() {
36+
final var escaped = """
37+
{\\"hello\\": \\"world\\"}""";
38+
3639
var nestedJson = """
3740
{"hello": "world"}""";
38-
assertEquals("""
39-
{\\"hello\\": \\"world\\"}""", JIUtil.escapeQuotes(nestedJson));
41+
assertEquals(escaped, JIUtil.escapeQuotes(nestedJson));
42+
assertEquals(escaped, JIUtil.escapeQuotesChecked(nestedJson));
4043

4144
nestedJson = """
4245
{"hello": "\\"world\\""}""";
43-
assertEquals("""
44-
{\\"hello\\": \\"\\"world\\"\\"}""", JIUtil.escapeQuotes(nestedJson));
46+
assertEquals(escaped, JIUtil.escapeQuotes(nestedJson));
47+
assertEquals(escaped, JIUtil.escapeQuotesChecked(nestedJson));
48+
49+
assertTrue(escaped == JIUtil.escapeQuotes(escaped));
50+
assertTrue(escaped == JIUtil.escapeQuotesChecked(escaped));
4551
}
4652

4753
@ParameterizedTest

0 commit comments

Comments
 (0)