<commit_before>package spinoco.protocol.mgcp.codec

import scodec.{Attempt, Codec, DecodeResult, Err, SizeBound}
import scodec.bits.BitVector
import scodec.codecs._
import shapeless.{:+:, CNil, Coproduct, Inl, Inr}
import spinoco.protocol.mgcp._
import spinoco.protocol.mgcp.MGCPParameter._
import spinoco.protocol.common.codec._
import spinoco.protocol.mgcp.BearerAttribute.{BearerEncoding, BearerExtensionName}
import spinoco.protocol.mgcp.CapabilityValue.{SupportedModes, SupportedPackages}
import spinoco.protocol.mgcp.LocalConnectionOption._

import scala.annotation.tailrec
import scala.concurrent.duration._


object MGCPParameterCodec {


  /**
    * A combinator, that first splits the stream by comma and then applies codec, stripping any whitespace after coma
    */
  def delimitedByComma[A](codec: Codec[A]):Codec[List[A]] = {
    listDelimited(BitVector.view(",".getBytes), takeWhileChar(dropWS ~> codec)(','))
  }

  /**
    * Like `delimitedByComma`, but codec is first applied, and then if there is another item (because of the next character being comma
    * then next codec is applied
    */
  def delimitedByComma2[A](codec: Codec[A]): Codec[List[A]] = {
    val commaSpace =  BitVector.view(", ".getBytes())
    val comma = ','.toByte
    @tailrec
    def go(acc: Vector[A], bs: BitVector): Attempt[DecodeResult[List[A]]] = {
      codec.decode(bs) match {
        case Attempt.Failure(err) => Attempt.failure(err)
        case Attempt.Successful(DecodeResult(a, rem)) =>

          if (rem.isEmpty) Attempt.successful(DecodeResult((acc :+ a).toList, BitVector.empty))
          else if (rem.getByte(0) != comma) Attempt.failure(Err("Separator must be `,` optionally followed by whitespace"))
          else {
            val next = rem.bytes.tail.dropWhile(_.toChar.isWhitespace)
            go(acc :+ a, next.bits)
          }

      }
    }

    new Codec[List[A]] {
      val encoder = listDelimited(commaSpace, codec)
      def sizeBound: SizeBound = SizeBound.unknown
      def encode(value: List[A]): Attempt[BitVector] = encoder.encode(value)
      def decode(bits: BitVector): Attempt[DecodeResult[List[A]]] = go(Vector.empty, bits)

    }
  }


  val responseAckCodec: Codec[ResponseAck] =  {
     val ackTxCodec: Codec[AckTx] =
       untilWs(transactionId :: optional(recover2(constant(BitVector.view("-".getBytes))), transactionId))
       .as[AckTx]

     delimitedByComma(ackTxCodec)
     .as[ResponseAck]
  }


  val bearerInformationCodec: Codec[BearerInformation] = {
    val baCodec: Codec[BearerAttribute] = {
      choice(
        (constant(BitVector("e:".getBytes)) :: mappedEnum(ascii, BearerEncodingType.values.map { v => v -> v.toString}.toMap)).as[BearerEncoding].upcast
        , ascii.as[BearerExtensionName].upcast
      )
    }

    delimitedByComma(baCodec)
    .as[BearerInformation]
  }




  val callIdCodec: Codec[CallId] = {
    tagged[String, CallId](guard(ascii)(is32Hex("CallId")))
    .as[CallId]
  }

  val connectionIdParameterCodec: Codec[ConnectionId] = {
    connectionIdCodec.as[ConnectionId]
  }

  val notifiedEntityCodec: Codec[NotifiedEntity] = {
    val entityNameCodec: Codec[EntityName] = {

      val withLocalPart =
        (
          ("LocalName" | takeWhileChar(localEndpointNameCodec)('@') <~ constantString("@"))
            .xmap[Some[LocalEndpointName]](Some(_), _.value).upcast[Option[LocalEndpointName]] ::
            ("Domain" | takeWhileChar(domainCodec)(':')) ::
            ("Port" | optional(recover2(constant(BitVector(":".getBytes))), intAsString))
          ).as[EntityName]

      val domainOnly =
        (
          ("LocalName" | provide(None).upcast[Option[LocalEndpointName]]) ::
          ("Domain" | takeWhileChar(domainCodec)(':')) ::
          ("Port" | optional(recover2(constant(BitVector(":".getBytes))), intAsString))
        ).as[EntityName]

      choice(
        withLocalPart
        , domainOnly
      )
    }

    entityNameCodec
    .as[NotifiedEntity]
  }

  val requestIdentifierCodec: Codec[RequestIdentifier] = {
    tagged[String, RequestIdentifier](guard(ascii)(is32Hex("RequestIdentifier")))
    .as[RequestIdentifier]
  }

  val localOptionCodec: Codec[LocalConnectionOption] = {
    val compressionAlgorithmCodec: Codec[CompressionAlgorithm] =
      ascii.as[CompressionAlgorithm]

    val bandwidthCodec: Codec[Bandwidth] =
      (intAsString :: optional(recover2(constant(BitVector.view("-".getBytes))), intAsString)).as[Bandwidth]

    val echoCancelCodec: Codec[EchoCancel] =
      mappedEnum(ascii, Map(true -> "on", false -> "off" ))
        .as[EchoCancel]

    val packetizationPeriodCodec: Codec[PacketizationPeriod] =
      intAsString.as[PacketizationPeriod]

    val gainControlCodec: Codec[GainControl] =
      choice[Option[Int]](
        constant(BitVector.view("auto".getBytes)).xmap[None.type](_ => None, _ => ()).upcast
        , intAsString.xmap[Some[Int]](Some(_), _.value).upcast
      ).as[GainControl]

    val silenceSuppressionCodec: Codec[SilenceSuppression] =
      mappedEnum(ascii, Map(true -> "on", false -> "off" ))
        .as[SilenceSuppression]


    val typeOfServiceCodec: Codec[TypeOfService] =
      ascii.as[TypeOfService]

    val resourceReservationCodec: Codec[ResourceReservation] =
      ascii.as[ResourceReservation]

    val typeOfNetworkCodec: Codec[TypeOfNetwork] = {
      val ntCodec = mappedEnum(ascii, NetworkType.values.map { v => v -> v.toString }.toMap)

      listDelimited(BitVector.view(";".getBytes), ntCodec).narrow(
        ls => {
          ls.headOption match {
            case None => Attempt.failure(Err("At least one type of the netowrk must be specified"))
            case Some(nt) => Attempt.successful(TypeOfNetwork(nt, ls.tail))
          }
        }
        , ton => ton.nt +: ton.supported
      )
    }


    val keyCodec: Codec[String] = takeWhile(ascii)(_ != ':'.asInstanceOf[Byte]) <~ constant(BitVector.view(":".getBytes))
<commit_msg>changed .value to .get<commit_after>package spinoco.protocol.mgcp.codec

import scodec.{Attempt, Codec, DecodeResult, Err, SizeBound}
import scodec.bits.BitVector
import scodec.codecs._
import shapeless.{:+:, CNil, Coproduct, Inl, Inr}
import spinoco.protocol.mgcp._
import spinoco.protocol.mgcp.MGCPParameter._
import spinoco.protocol.common.codec._
import spinoco.protocol.mgcp.BearerAttribute.{BearerEncoding, BearerExtensionName}
import spinoco.protocol.mgcp.CapabilityValue.{SupportedModes, SupportedPackages}
import spinoco.protocol.mgcp.LocalConnectionOption._

import scala.annotation.tailrec
import scala.concurrent.duration._


object MGCPParameterCodec {


  /**
    * A combinator, that first splits the stream by comma and then applies codec, stripping any whitespace after coma
    */
  def delimitedByComma[A](codec: Codec[A]):Codec[List[A]] = {
    listDelimited(BitVector.view(",".getBytes), takeWhileChar(dropWS ~> codec)(','))
  }

  /**
    * Like `delimitedByComma`, but codec is first applied, and then if there is another item (because of the next character being comma
    * then next codec is applied
    */
  def delimitedByComma2[A](codec: Codec[A]): Codec[List[A]] = {
    val commaSpace =  BitVector.view(", ".getBytes())
    val comma = ','.toByte
    @tailrec
    def go(acc: Vector[A], bs: BitVector): Attempt[DecodeResult[List[A]]] = {
      codec.decode(bs) match {
        case Attempt.Failure(err) => Attempt.failure(err)
        case Attempt.Successful(DecodeResult(a, rem)) =>

          if (rem.isEmpty) Attempt.successful(DecodeResult((acc :+ a).toList, BitVector.empty))
          else if (rem.getByte(0) != comma) Attempt.failure(Err("Separator must be `,` optionally followed by whitespace"))
          else {
            val next = rem.bytes.tail.dropWhile(_.toChar.isWhitespace)
            go(acc :+ a, next.bits)
          }

      }
    }

    new Codec[List[A]] {
      val encoder = listDelimited(commaSpace, codec)
      def sizeBound: SizeBound = SizeBound.unknown
      def encode(value: List[A]): Attempt[BitVector] = encoder.encode(value)
      def decode(bits: BitVector): Attempt[DecodeResult[List[A]]] = go(Vector.empty, bits)

    }
  }


  val responseAckCodec: Codec[ResponseAck] =  {
     val ackTxCodec: Codec[AckTx] =
       untilWs(transactionId :: optional(recover2(constant(BitVector.view("-".getBytes))), transactionId))
       .as[AckTx]

     delimitedByComma(ackTxCodec)
     .as[ResponseAck]
  }


  val bearerInformationCodec: Codec[BearerInformation] = {
    val baCodec: Codec[BearerAttribute] = {
      choice(
        (constant(BitVector("e:".getBytes)) :: mappedEnum(ascii, BearerEncodingType.values.map { v => v -> v.toString}.toMap)).as[BearerEncoding].upcast
        , ascii.as[BearerExtensionName].upcast
      )
    }

    delimitedByComma(baCodec)
    .as[BearerInformation]
  }




  val callIdCodec: Codec[CallId] = {
    tagged[String, CallId](guard(ascii)(is32Hex("CallId")))
    .as[CallId]
  }

  val connectionIdParameterCodec: Codec[ConnectionId] = {
    connectionIdCodec.as[ConnectionId]
  }

  val notifiedEntityCodec: Codec[NotifiedEntity] = {
    val entityNameCodec: Codec[EntityName] = {

      val withLocalPart =
        (
          ("LocalName" | takeWhileChar(localEndpointNameCodec)('@') <~ constantString("@"))
            .xmap[Some[LocalEndpointName]](Some(_), _.get).upcast[Option[LocalEndpointName]] ::
            ("Domain" | takeWhileChar(domainCodec)(':')) ::
            ("Port" | optional(recover2(constant(BitVector(":".getBytes))), intAsString))
          ).as[EntityName]

      val domainOnly =
        (
          ("LocalName" | provide(None).upcast[Option[LocalEndpointName]]) ::
          ("Domain" | takeWhileChar(domainCodec)(':')) ::
          ("Port" | optional(recover2(constant(BitVector(":".getBytes))), intAsString))
        ).as[EntityName]

      choice(
        withLocalPart
        , domainOnly
      )
    }

    entityNameCodec
    .as[NotifiedEntity]
  }

  val requestIdentifierCodec: Codec[RequestIdentifier] = {
    tagged[String, RequestIdentifier](guard(ascii)(is32Hex("RequestIdentifier")))
    .as[RequestIdentifier]
  }

  val localOptionCodec: Codec[LocalConnectionOption] = {
    val compressionAlgorithmCodec: Codec[CompressionAlgorithm] =
      ascii.as[CompressionAlgorithm]

    val bandwidthCodec: Codec[Bandwidth] =
      (intAsString :: optional(recover2(constant(BitVector.view("-".getBytes))), intAsString)).as[Bandwidth]

    val echoCancelCodec: Codec[EchoCancel] =
      mappedEnum(ascii, Map(true -> "on", false -> "off" ))
        .as[EchoCancel]

    val packetizationPeriodCodec: Codec[PacketizationPeriod] =
      intAsString.as[PacketizationPeriod]

    val gainControlCodec: Codec[GainControl] =
      choice[Option[Int]](
        constant(BitVector.view("auto".getBytes)).xmap[None.type](_ => None, _ => ()).upcast
        , intAsString.xmap[Some[Int]](Some(_), _.get).upcast
      ).as[GainControl]

    val silenceSuppressionCodec: Codec[SilenceSuppression] =
      mappedEnum(ascii, Map(true -> "on", false -> "off" ))
        .as[SilenceSuppression]


    val typeOfServiceCodec: Codec[TypeOfService] =
      ascii.as[TypeOfService]

    val resourceReservationCodec: Codec[ResourceReservation] =
      ascii.as[ResourceReservation]

    val typeOfNetworkCodec: Codec[TypeOfNetwork] = {
      val ntCodec = mappedEnum(ascii, NetworkType.values.map { v => v -> v.toString }.toMap)

      listDelimited(BitVector.view(";".getBytes), ntCodec).narrow(
        ls => {
          ls.headOption match {
            case None => Attempt.failure(Err("At least one type of the netowrk must be specified"))
            case Some(nt) => Attempt.successful(TypeOfNetwork(nt, ls.tail))
          }
        }
        , ton => ton.nt +: ton.supported
      )
    }


    val keyCodec: Codec[String] = takeWhile(ascii)(_ != ':'.asInstanceOf[Byte]) <~ constant(BitVector.view(":".getBytes))

{'fraction_non_alphanumeric': 0.13515105613060674, 'fraction_numerical': 0.0016830766641420516, 'mean_word_length': 3.5083459787556905, 'pattern_counts': {'":': 6, '<': 7, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2016995', 'n_tokens_mistral': 3898, 'n_tokens_neox': 3755, 'n_words': 663}