diff --git a/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala b/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala index 35b2f63694..a72ac59517 100644 --- a/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala +++ b/src/main/scala/org/bitcoins/core/util/BitcoinSUtil.scala @@ -89,6 +89,31 @@ trait BitcoinSUtil { val paddedHex = padding.mkString + hex paddedHex } + + /** Converts a sequence of bytes to a sequence of bit vectors - assumes the sequence of bytes are big endian */ + def bytesToBitVectors(bytes: Seq[Byte]): Seq[Seq[Boolean]] = bytes.map(byteToBitVector) + + /** Converts a byte to a bit vector representing that byte - the bit vector is big endian */ + def byteToBitVector(byte: Byte): Seq[Boolean] = { + (0 to 7).map(index => isBitSet(byte,7 - index)) + } + + /** Checks if the bit at the given index is set */ + def isBitSet(byte: Byte, index: Int): Boolean = ((byte >> index) & 1) == 1 + + /** Converts a bit vector to a single byte -- assumes the bits are big endian */ + def bitVectorToByte(bits: Seq[Boolean]): Byte = { + require(bits.size <= 8, "Cannot convert a bit vector to a byte when the size of the bit vector is larger than 8, got: " + bits) + val b = bits.reverse + val result: Seq[Int] = b.zipWithIndex.map { case (b, index) => + if (b) NumberUtil.pow2(index).toInt else 0 + } + result.sum.toByte + } + + /** Converts a sequence of bit vectors to a sequence of bytes */ + def bitVectorsToBytes(bits: Seq[Seq[Boolean]]): Seq[Byte] = bits.map(bitVectorToByte) + } object BitcoinSUtil extends BitcoinSUtil diff --git a/src/test/scala/org/bitcoins/core/util/BitcoinSUtilSpec.scala b/src/test/scala/org/bitcoins/core/util/BitcoinSUtilSpec.scala index 530360123b..9cccec8d89 100644 --- a/src/test/scala/org/bitcoins/core/util/BitcoinSUtilSpec.scala +++ b/src/test/scala/org/bitcoins/core/util/BitcoinSUtilSpec.scala @@ -1,6 +1,6 @@ package org.bitcoins.core.util -import org.bitcoins.core.gen.StringGenerators +import org.bitcoins.core.gen.{CryptoGenerators, NumberGenerator, StringGenerators} import org.scalacheck.{Gen, Prop, Properties} /** * Created by chris on 6/20/16. @@ -16,4 +16,15 @@ class BitcoinSUtilSpec extends Properties("BitcoinSUtilSpec") with BitcoinSLogge Prop.forAll(StringGenerators.hexString) { hex : String => BitcoinSUtil.flipEndianess(BitcoinSUtil.flipEndianess(hex)) == hex } + + property("Convert a byte to a bit vector, convert it back to the original byte") = + Prop.forAll { byte: Byte => + BitcoinSUtil.bitVectorToByte(BitcoinSUtil.byteToBitVector(byte)) == byte + } + + property("Convert a sequence of bit vectors to a sequence of bytes") = + Prop.forAll(NumberGenerator.bitVectors) { bitVectors: Seq[Seq[Boolean]] => + BitcoinSUtil.bytesToBitVectors(BitcoinSUtil.bitVectorsToBytes(bitVectors)) == bitVectors + + } } diff --git a/src/test/scala/org/bitcoins/core/util/BitcoinSUtilTest.scala b/src/test/scala/org/bitcoins/core/util/BitcoinSUtilTest.scala index 56370b1ae8..04e55e3c1c 100644 --- a/src/test/scala/org/bitcoins/core/util/BitcoinSUtilTest.scala +++ b/src/test/scala/org/bitcoins/core/util/BitcoinSUtilTest.scala @@ -21,4 +21,36 @@ class BitcoinSUtilTest extends FlatSpec with MustMatchers { //fail to parse a hex string that is uneven BitcoinSUtil.isHex("123") must be (false) } + + it must "convert a byte to a bit vector" in { + val byte = 0.toByte + BitcoinSUtil.byteToBitVector(byte) must be (Seq(false,false,false,false,false,false,false,false)) + + val byte1 = 1.toByte + BitcoinSUtil.byteToBitVector(byte1) must be (Seq(false,false,false,false,false,false,false,true)) + + val byte2 = 2.toByte + BitcoinSUtil.byteToBitVector(byte2) must be (Seq(false,false,false,false,false,false,true,false)) + + val byte3 = 3.toByte + BitcoinSUtil.byteToBitVector(byte3) must be (Seq(false,false,false,false,false,false,true,true)) + + val maxByte = 0xff.toByte + BitcoinSUtil.byteToBitVector(maxByte) must be (Seq(true,true,true,true,true,true,true,true)) + } + + it must "convert a bit vector to a byte" in { + val bitVector0 = Seq(false,false,false,false,false,false,false,false) + BitcoinSUtil.bitVectorToByte(bitVector0) must be (0.toByte) + + val bitVector1 = Seq(false,false,false,false,false,false,false,true) + BitcoinSUtil.bitVectorToByte(bitVector1) must be (1.toByte) + + val bitVector2 = Seq(false,false,false,false,false,false,true,false) + BitcoinSUtil.bitVectorToByte(bitVector2) must be (2.toByte) + + val bitVectorMax = Seq(true,true,true,true,true,true,true,true) + BitcoinSUtil.bitVectorToByte(bitVectorMax) must be (0xff.toByte) + + } }