3

I am trying to decode a Base64 encoded Image in Scala. The string literal is over 65535 characters long. Whenever I try to compile the below code, the compiler gives an IllegalArgumentException.

Scala Code:

val data = "/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAA ... "
val imageByte = Base64.getDecoder.decode(data)        
val byteArray = new ByteArrayInputStream(imageByte)       
val image = ImageIO.read(byteArray)                       
ImageIO.write(image, "jpeg", new File("image.jpeg"))

Compiler Exception:

Error:scalac: Error:         org.jetbrains.jps.incremental.scala.remote.ServerException
java.lang.IllegalArgumentException
at scala.tools.asm.ByteVector.putUTF8(ByteVector.java:213)
at scala.tools.asm.ClassWriter.newUTF8(ClassWriter.java:1092)
at scala.tools.asm.ClassWriter.newString(ClassWriter.java:1525)
at scala.tools.asm.ClassWriter.newConstItem(ClassWriter.java:1042)
at scala.tools.asm.MethodWriter.visitLdcInsn(MethodWriter.java:1134)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.genConstant(GenASM.scala:1582)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.scala$tools$nsc$backend$jvm$GenASM$JPlainBuilder$$genInstr$1(GenASM.scala:2296)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder$$anonfun$genBlock$1$2.apply(GenASM.scala:2227)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder$$anonfun$genBlock$1$2.apply(GenASM.scala:2213)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at scala.tools.nsc.backend.icode.BasicBlocks$BasicBlock.foreach(BasicBlocks.scala:195)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.genBlock$1(GenASM.scala:2213)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.genBlocks$1(GenASM.scala:2151)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.genCode(GenASM.scala:2746)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.genMethod(GenASM.scala:1471)
at scala.tools.nsc.backend.jvm.GenASM$JPlainBuilder.genClass(GenASM.scala:1341)
at scala.tools.nsc.backend.jvm.GenASM$AsmPhase.emitFor$1(GenASM.scala:198)
at scala.tools.nsc.backend.jvm.GenASM$AsmPhase.run(GenASM.scala:204)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1501)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1486)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1481)
at scala.tools.nsc.Global$Run.compile(Global.scala:1582)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:115)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:94)
at xsbt.CompilerInterface.run(CompilerInterface.scala:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:101)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:47)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
at org.jetbrains.jps.incremental.scala.local.IdeaIncrementalCompiler.compile(IdeaIncrementalCompiler.scala:29)
at org.jetbrains.jps.incremental.scala.local.LocalServer.compile(LocalServer.scala:26)
at org.jetbrains.jps.incremental.scala.remote.Main$.make(Main.scala:67)
at org.jetbrains.jps.incremental.scala.remote.Main$.nailMain(Main.scala:24)
at org.jetbrains.jps.incremental.scala.remote.Main.nailMain(Main.scala)
at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.martiansoftware.nailgun.NGSession.run(NGSession.java:319)

As per this bug, I understand that according to JVM spec, the byte length greater than 2-byte field won't fit. After an exhaustive search, I am unable to find a workaround for this issue with the bug still active. What could be a fine workaround to deal with long string literals in Java/Scala.

Sudhakar
  • 43
  • 9

2 Answers2

5

When is a literal is this long it should really not be embedded into the code anymore, because of readability issues. I would load it from a file/resource.

Java

With Commons IO this is really easy to do:

String str = IOUtils.toString(this.getClass().getResource("myResource", StandardCharsets.UTF_8);

Slightly less readable in my opinion, but if you really don't want to use any libraries you can also do this starting from Java 7:

String str = new String(Files.readAllBytes(Paths.get("myResource")), StandardCharsets.UTF_8);

Scala

In scala you can use the build-in scala.io.Source class:

val testTxtSource = Source.fromFile("myResource")
val str = testTxtSource.mkString()
testTxtSource.close()
Ben Reich
  • 16,222
  • 2
  • 38
  • 59
Todd Sewell
  • 1,444
  • 12
  • 26
  • 1
    Why to use Commons IO, if Java 1.7 has `Files` class: `String s = new String( Files.readAllBytes(Paths.get("myResource")), Charset.defaultCharset());` – matoni Feb 12 '17 at 19:07
  • @matoni Thanks! I added your way too. – Todd Sewell Feb 12 '17 at 19:13
  • I would also mention `scala.io.Source`, which however require a few lines of code to be used properly (handling errors and freeing resources). You can read more here: http://stackoverflow.com/questions/4458864/whats-the-right-way-to-use-scala-io-source – stefanobaghino Feb 13 '17 at 08:37
  • @stefanobaghino I added that one too, thanks! Feel free to edit if there are any problems with it, I'm not a Scala programmer. – Todd Sewell Feb 13 '17 at 08:54
0

If you can read four bytes at a time, this will correspond to precisely three bytes once decoded. Concatenate them together, and you will get the same result.

Joe C
  • 15,324
  • 8
  • 38
  • 50