summaryrefslogtreecommitdiff
path: root/src/ConversionUtils.scala
blob: 32689fc1d722e5043b0470580e6f9c748d6ff220 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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()
  }
}