def withEncoding()

in bijection-core/src/main/scala/com/twitter/bijection/StringInjections.scala [31:73]


  def withEncoding(encoding: String): Injection[String, Array[Byte]] =
    new AbstractInjection[String, Array[Byte]] {
      @transient private[this] var decRef: AtomicSharedState[(CharsetDecoder, CharBuffer)] = null

      private[this] def mkSharedState =
        new AtomicSharedState({ () =>
          val dec =
            Charset.forName(encoding).newDecoder.onUnmappableCharacter(CodingErrorAction.REPORT)
          val buf = CharBuffer.allocate(1024) // something big enough, if not big enough, allocate
          (dec, buf)
        })

      def apply(s: String) = s.getBytes(encoding)
      override def invert(b: Array[Byte]) =
        // toString on ByteBuffer is nicer, so use it
        attempt(ByteBuffer.wrap(b)) { bb =>
          // these are mutable, so it can't be shared trivially
          // avoid GC pressure and (probably) perform better
          if (null == decRef) {
            decRef = mkSharedState
          }
          val decBuf = decRef.get
          val dec = decBuf._1
          val buf = decBuf._2
          val maxSpace = (b.length * (dec.maxCharsPerByte.toDouble)).toInt + 1
          val thisBuf = if (maxSpace > buf.limit()) CharBuffer.allocate(maxSpace) else buf

          // this is the error free result
          @inline def assertUnderFlow(cr: CoderResult): Unit =
            if (!cr.isUnderflow) cr.throwException

          assertUnderFlow(dec.reset.decode(bb, thisBuf, true))
          assertUnderFlow(dec.flush(thisBuf))
          // set the limit to be the position
          thisBuf.flip
          val str = thisBuf.toString
          // make sure the buffer we store is clear.
          buf.clear
          // we don't reset with the larger buffer to avoid memory leaks
          decRef.release(decBuf)
          str
        }
    }