All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gatling.http.util.ByteBuffersDecoder.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/**
 * Copyright 2011-2016 GatlingCorp (http://gatling.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gatling.http.util

import java.nio.ByteBuffer
import java.nio.charset.{ CharacterCodingException, Charset }
import java.nio.charset.StandardCharsets._
import java.util.concurrent.ConcurrentHashMap
import java.util.function.{ Function => JFunction }

import scala.compat.java8.FunctionConverters._

import io.gatling.commons.util.Collections._

object ByteBuffersDecoder {

  private[this] val utf8Decoders = new ThreadLocal[ByteBuffersDecoder] {
    override def initialValue = new Utf8ByteBuffersDecoder
  }

  private[this] val usAsciiDecoders = new ThreadLocal[ByteBuffersDecoder] {
    override def initialValue = new UsAsciiByteBuffersDecoder
  }

  private[this] val newCharsetDecodersScalaFunction: Charset => ThreadLocal[ByteBuffersDecoder] = charset => new ThreadLocal[ByteBuffersDecoder] {
    override def initialValue = new CharsetDecoderByteBuffersDecoder(charset)
  }
  private[this] val newCharsetDecoders: JFunction[Charset, ThreadLocal[ByteBuffersDecoder]] = newCharsetDecodersScalaFunction.asJava

  private[this] val otherDecoders = new ConcurrentHashMap[Charset, ThreadLocal[ByteBuffersDecoder]]()

  private[this] def decoder(charset: Charset): ByteBuffersDecoder = charset match {
    case UTF_8    => utf8Decoders.get()
    case US_ASCII => usAsciiDecoders.get()
    case _        => otherDecoders.computeIfAbsent(charset, newCharsetDecoders).get()
  }

  def decode(bufs: Seq[ByteBuffer], charset: Charset): String =
    decoder(charset).decode(bufs)
}

sealed trait ByteBuffersDecoder {

  def decode(bufs: Seq[ByteBuffer]): String
}

class CharsetDecoderByteBuffersDecoder(charset: Charset) extends ByteBuffersDecoder {

  private[this] def mergeByteBuffers(bufs: Seq[ByteBuffer]): Array[Byte] = {

    val len = bufs.sumBy(_.remaining)
    val bytes = new Array[Byte](len)
    val merged = ByteBuffer.wrap(bytes)
    bufs.foreach(merged.put)
    bytes
  }

  override def decode(bufs: Seq[ByteBuffer]): String =
    new String(mergeByteBuffers(bufs), charset)
}

class UsAsciiByteBuffersDecoder extends ByteBuffersDecoder {

  private val sb = new StringBuilder

  override def decode(bufs: Seq[ByteBuffer]): String = {
    sb.setLength(0)
    bufs.foreach { buf =>
      while (buf.remaining > 0) {
        sb.append(buf.get().toChar)
      }
    }
    sb.toString
  }
}

object Utf8ByteBuffersDecoder {
  val Utf8Accept = 0
  val Utf8Reject = 12

  val Types = Array[Byte](
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
  )

  val States = Array[Byte](
    0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
    12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12,
    12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12,
    12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12,
    12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
  )
}

class Utf8ByteBuffersDecoder extends ByteBuffersDecoder {

  import Utf8ByteBuffersDecoder._

  private val sb = new StringBuilder
  private var state = Utf8Accept
  private var codep = 0

  private def write(b: Byte): Unit = {
    val t = Types(b & 0xFF)

    codep = if (state != Utf8Accept) (b & 0x3f) | (codep << 6) else (0xff >> t) & b
    state = States(state + t)

    if (state == Utf8Accept) {
      if (codep < Character.MIN_HIGH_SURROGATE) {
        sb.append(codep.toChar)
      } else {
        Character.toChars(codep).foreach(sb.append)
      }
    } else if (state == Utf8Reject) {
      throw new CharacterCodingException
    }
  }

  override def decode(bufs: Seq[ByteBuffer]): String = {

    sb.setLength(0)
    state = Utf8Accept
    codep = 0

    bufs.foreach { buf =>
      while (buf.remaining > 0) {
        write(buf.get())
      }
    }

    if (state == Utf8Accept) {
      sb.toString
    } else {
      throw new CharacterCodingException
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy