package ixee.cryptopals.utils import ByteUtils._ import FunctionUtils._ object ConversionUtils { def hexStr2Bytes(s: String): Seq[Byte] = padToByte(s).grouped(2).map(byteStr2Byte).toStream.force def byteStr2Byte(str: String): Byte = charSeq2Byte(str.toSeq) def charSeq2Byte(seq: Seq[Char]): Byte = seq.map(octet2nibble).reduce(joinNibbles) def joinNibbles(a: Byte, b: Byte): Byte = ((a @<< 4) @+ b) def padToByte(s: String): String = if (s.length % 2 != 0) "0" + s else s def octet2nibble(c: Char): Byte = { (c.toLower match { case c if c >= 'a' && c <= 'f' => (c - 'a') + 10.toByte case c if c >= '0' && c <= '9' => c - '0' case _ => throw new IllegalArgumentException(s"Invalid hexadecimal character: $c") }).toByte } def hexStr2Base64String(s: String): String = { import io.github.marklister.base64.Base64._ hexStr2Bytes(s).toArray.toBase64 } implicit class RichSeqByte(seq: Seq[Byte]) { def to[T : SizedNumeric : BitOps]: T = { val numeric = implicitly[SizedNumeric[T]] if(seq.length < numeric.byteSize) { throw new RuntimeException("Byte input is not long enough") } var out: Long = seq(0) for(i <- 1 until numeric.byteSize) { out << 8 out = seq(i) | out } numeric.fromLong(out) } def hex: String = seq.map(_.hex).reduceLeft(_ + _) def asAscii: String = new String(seq.toArray) } implicit class RichStringBytes(s: String) { def asBytes = s.toSeq.map(_.toByte) def asByteStream = s.asBytes.toStream def asRepeatedBytes(count: Int) = Stream.continually(s.asBytes).flatten.take(count) } // TODO: tailrec this, get rid of for def toByteSeq[T : SizedNumeric : BitOps](x: T): Seq[Byte] = { val buf = new Array[Byte](x.byteSize) for(i <- 0 until x.byteSize) { val shiftAmount = (x.byteSize - i - 1) << 3 buf(i) = (x @>>> shiftAmount).truncatedTo[Byte] } buf.toSeq } def toBinaryString[T : SizedNumeric](x: T)(implicit a: BitOps[T]): String = { val buf = new StringBuilder(x.bitSize) for(i <- 0 until x.bitSize) { val shiftAmount: Int = x.bitSize - i - 1 buf.append((x @>>> shiftAmount) @& 0x01.liftedTo[T]) } buf.toString() } }