diff --git a/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala b/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala index 8f05e281..aa4988da 100644 --- a/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala +++ b/zio-json/shared/src/main/scala/zio/json/internal/lexer.scala @@ -85,20 +85,8 @@ object Lexer { c = in.readChar() c != '"' }) { - if (c == '\\') { - (in.readChar(): @switch) match { - case '"' => c = '"' - case '\\' => c = '\\' - case '/' => c = '/' - case 'b' => c = '\b' - case 'f' => c = '\f' - case 'n' => c = '\n' - case 'r' => c = '\r' - case 't' => c = '\t' - case 'u' => c = nextHex4(trace, in) - case _ => error(c, trace) - } - } else if (c < ' ') error("invalid control in string", trace) + if (c == '\\') c = nextEscaped(trace, in) + else if (c < ' ') error("invalid control in string", trace) bs = matrix.update(bs, i, c) i += 1 } @@ -181,7 +169,7 @@ object Lexer { case 'r' => '\r' case 't' => '\t' case 'u' => Lexer.nextHex4(trace, in) - case _ => Lexer.error(c, trace) + case c => Lexer.error(c, trace) }).toInt } else if (c == '\\') { escaped = true @@ -209,20 +197,8 @@ object Lexer { c = in.readChar() c != '"' }) { - if (c == '\\') { - (in.readChar(): @switch) match { - case '"' => c = '"' - case '\\' => c = '\\' - case '/' => c = '/' - case 'b' => c = '\b' - case 'f' => c = '\f' - case 'n' => c = '\n' - case 'r' => c = '\r' - case 't' => c = '\t' - case 'u' => c = nextHex4(trace, in) - case _ => error(c, trace) - } - } else if (c < ' ') error("invalid control in string", trace) + if (c == '\\') c = nextEscaped(trace, in) + else if (c < ' ') error("invalid control in string", trace) if (i == cs.length) cs = java.util.Arrays.copyOf(cs, i << 1) cs(i) = c i += 1 @@ -236,28 +212,29 @@ object Lexer { c = in.readChar() if ( c == '"' || { - if (c == '\\') { - (in.readChar(): @switch) match { - case '"' => c = '"' - case '\\' => c = '\\' - case '/' => c = '/' - case 'b' => c = '\b' - case 'f' => c = '\f' - case 'n' => c = '\n' - case 'r' => c = '\r' - case 't' => c = '\t' - case 'u' => c = nextHex4(trace, in) - case _ => error(c, trace) - } - } else if (c < ' ') error("invalid control in string", trace) + if (c == '\\') c = nextEscaped(trace, in) + else if (c < ' ') error("invalid control in string", trace) in.readChar() != '"' } ) error("expected single character string", trace) c } - // consumes 4 hex characters after current - @noinline def nextHex4(trace: List[JsonError], in: OneCharReader): Char = { + @noinline private[this] def nextEscaped(trace: List[JsonError], in: OneCharReader): Char = + (in.readChar(): @switch) match { + case '"' => '"' + case '\\' => '\\' + case '/' => '/' + case 'b' => '\b' + case 'f' => '\f' + case 'n' => '\n' + case 'r' => '\r' + case 't' => '\t' + case 'u' => nextHex4(trace, in) + case c => error(c, trace) + } + + def nextHex4(trace: List[JsonError], in: OneCharReader): Char = { var i, accum = 0 while (i < 4) { val c = in.readChar() diff --git a/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala b/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala index 014105de..84949c6a 100644 --- a/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala @@ -16,6 +16,25 @@ object DecoderSpec extends ZIOSpecDefault { val spec: Spec[Environment, Any] = suite("Decoder")( suite("fromJson")( + test("string") { + assert(""""abc"""".fromJson[String])(isRight(equalTo("abc"))) && + assert(""""abc\n"""".fromJson[String])(isRight(equalTo("abc\n"))) && + assert("\"abc\\u0182\"".fromJson[String])(isRight(equalTo("abcƂ"))) && + assert("\"abc\\u1Ee1\"".fromJson[String])(isRight(equalTo("abcỡ"))) && + assert(""""abc\x"""".fromJson[String])(isLeft(equalTo("""(invalid '\x' in string)"""))) && + assert("\"\u0000\"".fromJson[String])(isLeft(equalTo("""(invalid control in string)"""))) && + assert("\"\\u0000\"".replace('0', 'g').fromJson[String])(isLeft(equalTo("""(invalid charcode in string)"""))) + }, + test("char") { + assert(""""a"""".fromJson[Char])(isRight(equalTo('a'))) && + assert(""""\n"""".fromJson[Char])(isRight(equalTo('\n'))) && + assert("\"\\u0182\"".fromJson[Char])(isRight(equalTo('Ƃ'))) && + assert("\"\\u1Ee1\"".fromJson[Char])(isRight(equalTo('ỡ'))) && + assert(""""aa"""".fromJson[Char])(isLeft(equalTo("""(expected single character string)"""))) && + assert(""""\x"""".fromJson[Char])(isLeft(equalTo("""(invalid '\x' in string)"""))) && + assert("\"\u0000\"".fromJson[Char])(isLeft(equalTo("""(invalid control in string)"""))) && + assert("\"\\u0000\"".replace('0', 'g').fromJson[Char])(isLeft(equalTo("""(invalid charcode in string)"""))) + }, test("byte") { assert("-123".fromJson[Byte])(isRight(equalTo(-123: Byte))) && assert("123".fromJson[Byte])(isRight(equalTo(123: Byte))) &&