Skip to content
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
9 changes: 5 additions & 4 deletions sjsonnet/src-js/sjsonnet/CharSWAR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ object CharSWAR {
false
}

def isAsciiJsonSafe(s: String): Boolean = {
var i = 0
val len = s.length
while (i < len) {
def isAsciiJsonSafe(s: String): Boolean = isAsciiJsonSafe(s, 0, s.length)

def isAsciiJsonSafe(s: String, from: Int, to: Int): Boolean = {
var i = from
while (i < to) {
val c = s.charAt(i)
if (c < 32 || c == '"' || c == '\\' || c >= 128) return false
i += 1
Expand Down
3 changes: 3 additions & 0 deletions sjsonnet/src-js/sjsonnet/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ object Platform {
def isAsciiJsonSafe(s: String): Boolean =
CharSWAR.isAsciiJsonSafe(s)

def isAsciiJsonSafe(s: String, from: Int, to: Int): Boolean =
CharSWAR.isAsciiJsonSafe(s, from, to)

private def nodeToJson(node: Node): ujson.Value = node match {
case _: Node.ScalarNode =>
YamlDecoder.forAny.construct(node).getOrElse("") match {
Expand Down
22 changes: 15 additions & 7 deletions sjsonnet/src-jvm/sjsonnet/CharSWAR.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,20 @@ static boolean hasEscapeChar(byte[] arr, int from, int to) {
* encoding step: all chars must be printable ASCII excluding {@code '"'} and {@code '\\'}.
*/
static boolean isAsciiJsonSafe(String str) {
int len = str.length();
return isAsciiJsonSafe(str, 0, str.length());
}

/**
* Range variant of {@link #isAsciiJsonSafe(String)}. Used by the format parser to scan literal
* windows of a format string without allocating substrings.
*/
static boolean isAsciiJsonSafe(String str, int from, int to) {
int len = to - from;
if (len < 8) {
return isAsciiJsonSafeScalar(str, len);
return isAsciiJsonSafeScalar(str, from, to);
}
int i = 0;
int limit = len - 3; // 4 UTF-16 chars per word
int i = from;
int limit = to - 3; // 4 UTF-16 chars per word
while (i < limit) {
long word =
((long) str.charAt(i)) |
Expand All @@ -106,7 +114,7 @@ static boolean isAsciiJsonSafe(String str) {
if (swarHasUnsafeAsciiChar(word)) return false;
i += 4;
}
while (i < len) {
while (i < to) {
char c = str.charAt(i);
if (c < 32 || c == '"' || c == '\\' || c >= 128) return false;
i++;
Expand Down Expand Up @@ -216,8 +224,8 @@ private static boolean hasEscapeCharScalar(String s, int len) {
return false;
}

private static boolean isAsciiJsonSafeScalar(String s, int len) {
for (int i = 0; i < len; i++) {
private static boolean isAsciiJsonSafeScalar(String s, int from, int to) {
for (int i = from; i < to; i++) {
char c = s.charAt(i);
if (c < 32 || c == '"' || c == '\\' || c >= 128) return false;
}
Expand Down
3 changes: 3 additions & 0 deletions sjsonnet/src-jvm/sjsonnet/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ object Platform {
def isAsciiJsonSafe(s: String): Boolean =
CharSWAR.isAsciiJsonSafe(s)

def isAsciiJsonSafe(s: String, from: Int, to: Int): Boolean =
CharSWAR.isAsciiJsonSafe(s, from, to)

def gzipBytes(b: Array[Byte]): String = {
val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream(b.length)
val gzip: GZIPOutputStream = new GZIPOutputStream(outputStream)
Expand Down
20 changes: 11 additions & 9 deletions sjsonnet/src-native/sjsonnet/CharSWAR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,14 @@ object CharSWAR {
false
}

def isAsciiJsonSafe(s: String): Boolean = {
val len = s.length
if (len < 8) return isAsciiJsonSafeScalar(s, len)
def isAsciiJsonSafe(s: String): Boolean = isAsciiJsonSafe(s, 0, s.length)

var i = 0
val limit = len - 3
def isAsciiJsonSafe(s: String, from: Int, to: Int): Boolean = {
val len = to - from
if (len < 8) return isAsciiJsonSafeScalar(s, from, to)

var i = from
val limit = to - 3
while (i < limit) {
val word =
(s.charAt(i).toLong) |
Expand All @@ -88,7 +90,7 @@ object CharSWAR {
if (swarHasUnsafeAsciiChar(word)) return false
i += 4
}
while (i < len) {
while (i < to) {
val c = s.charAt(i)
if (c < 32 || c == '"' || c == '\\' || c >= 128) return false
i += 1
Expand Down Expand Up @@ -166,9 +168,9 @@ object CharSWAR {
@inline private def zero16(word: Long): Long =
~((word & U16_HOLE) + U16_HOLE | word | U16_HOLE)

@inline private def isAsciiJsonSafeScalar(s: String, len: Int): Boolean = {
var i = 0
while (i < len) {
@inline private def isAsciiJsonSafeScalar(s: String, from: Int, to: Int): Boolean = {
var i = from
while (i < to) {
val c = s.charAt(i)
if (c < 32 || c == '"' || c == '\\' || c >= 128) return false
i += 1
Expand Down
3 changes: 3 additions & 0 deletions sjsonnet/src-native/sjsonnet/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ object Platform {
def isAsciiJsonSafe(s: String): Boolean =
CharSWAR.isAsciiJsonSafe(s)

def isAsciiJsonSafe(s: String, from: Int, to: Int): Boolean =
CharSWAR.isAsciiJsonSafe(s, from, to)

def gzipBytes(b: Array[Byte]): String = {
val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream(b.length)
val gzip: GZIPOutputStream = new GZIPOutputStream(outputStream)
Expand Down
13 changes: 3 additions & 10 deletions sjsonnet/src/sjsonnet/Format.scala
Original file line number Diff line number Diff line change
Expand Up @@ -294,20 +294,13 @@ object Format {
}

/**
* Scalar ASCII-JSON-safe check over a substring window of `s`. Matches the predicate used by
* ASCII-JSON-safe check over a substring window of `s`. Matches the predicate used by
* [[Platform.isAsciiJsonSafe]] (printable ASCII, no `"` or `\`). Used at format-parse time so the
* result can be cached on [[RuntimeFormat]] and combined with per-value ASCII-safety at format
* time.
*/
private def isAsciiJsonSafeRange(s: String, from: Int, to: Int): Boolean = {
var i = from
while (i < to) {
val c = s.charAt(i)
if (c < 32 || c == '"' || c == '\\' || c >= 128) return false
i += 1
}
true
}
private def isAsciiJsonSafeRange(s: String, from: Int, to: Int): Boolean =
Platform.isAsciiJsonSafe(s, from, to)

/**
* Hand-written format string scanner. Replaces the fastparse-based parser with direct
Expand Down
Loading
Loading