How can I convert IP range to Cidr in C#? - c#

There has lots of example of convert CIDR to ip range. But I want to know how can I use start/end ip address to generate a/some cidr in C#?
for example:
I have start ip address(192.168.0.1) and end ip address(192.168.0.254). So use these two address to generate cidr list {192.168.0.0/31, 192.168.0.2/32}. Is there any C# code example?

CIDR class with static methods to split an IP range into a minimal set of disjoint CIDR ranges, which cover exactly the original IP range.
The split methods (the "real" one working on BigIntegers doing the actual work, and the wrapper for IP addresses and CIDR creation) are at the bottom.
Use with foreach (IPRangeToCidr.CIDR c in IPRangeToCidr.CIDR.split(first, last)) ...
Requires System.Numerics.dll in the references.
using System;
using System.Numerics;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
namespace IPRangeToCidr {
public struct CIDR {
private IPAddress address;
private uint network_length, bits;
public CIDR(IPAddress address, uint network_length) {
this.address = address;
this.network_length = network_length;
this.bits = AddressFamilyBits(address.AddressFamily);
if (network_length > bits) {
throw new ArgumentException("Invalid network length " + network_length + " for " + address.AddressFamily);
}
}
public IPAddress NetworkAddress {
get { return address; }
}
public IPAddress LastAddress {
get { return IPAddressAdd(address, (new BigInteger(1) << (int) HostLength) - 1); }
}
public uint NetworkLength {
get { return network_length; }
}
public uint AddressBits {
get { return bits; }
}
public uint HostLength {
get { return bits - network_length; }
}
override public String ToString() {
return address.ToString() + "/" + NetworkLength.ToString();
}
public String ToShortString() {
if (network_length == bits) return address.ToString();
return address.ToString() + "/" + NetworkLength.ToString();
}
/* static helpers */
public static IPAddress IPAddressAdd(IPAddress address, BigInteger i) {
return IPFromUnsigned(IPToUnsigned(address) + i, address.AddressFamily);
}
public static uint AddressFamilyBits(AddressFamily family) {
switch (family) {
case AddressFamily.InterNetwork:
return 32;
case AddressFamily.InterNetworkV6:
return 128;
default:
throw new ArgumentException("Invalid address family " + family);
}
}
private static BigInteger IPToUnsigned(IPAddress addr) {
/* Need to reverse addr bytes for BigInteger; prefix with 0 byte to force unsigned BigInteger
* read BigInteger bytes as: bytes[n] bytes[n-1] ... bytes[0], address is bytes[0] bytes[1] .. bytes[n] */
byte[] b = addr.GetAddressBytes();
byte[] unsigned = new byte[b.Length + 1];
for (int i = 0; i < b.Length; ++i) {
unsigned[i] = b[(b.Length - 1) - i];
}
unsigned[b.Length] = 0;
return new BigInteger(unsigned);
}
private static byte[] GetUnsignedBytes(BigInteger unsigned, uint bytes) {
/* reverse bytes again. check that now higher bytes are actually used */
if (unsigned.Sign < 0) throw new ArgumentException("argument must be >= 0");
byte[] data = unsigned.ToByteArray();
byte[] result = new byte[bytes];
for (int i = 0; i < bytes && i < data.Length; ++i) {
result[bytes - 1 - i] = data[i];
}
for (uint i = bytes; i < data.Length; ++i) {
if (data[i] != 0) throw new ArgumentException("argument doesn't fit in requested number of bytes");
}
return result;
}
private static IPAddress IPFromUnsigned(BigInteger unsigned, System.Net.Sockets.AddressFamily family) {
/* IPAddress(byte[]) constructor picks family from array size */
switch (family) {
case System.Net.Sockets.AddressFamily.InterNetwork:
return new IPAddress(GetUnsignedBytes(unsigned, 4));
case System.Net.Sockets.AddressFamily.InterNetworkV6:
return new IPAddress(GetUnsignedBytes(unsigned, 16));
default:
throw new ArgumentException("AddressFamily " + family.ToString() + " not supported");
}
}
/* splits set [first..last] of unsigned integers into disjoint slices { x,..., x + 2^k - 1 | x mod 2^k == 0 }
* covering exaclty the given set.
* yields the slices ordered by x as tuples (x, k)
* This code relies on the fact that BigInteger can't overflow; temporary results may need more bits than last is using.
*/
public static IEnumerable<Tuple<BigInteger, uint>> split(BigInteger first, BigInteger last) {
if (first > last) yield break;
if (first < 0) throw new ArgumentException();
last += 1;
/* mask == 1 << len */
BigInteger mask = 1;
uint len = 0;
while (first + mask <= last) {
if ((first & mask) != 0) {
yield return new Tuple<BigInteger, uint>(first, len);
first += mask;
}
mask <<= 1;
++len;
}
while (first < last) {
mask >>= 1;
--len;
if ((last & mask) != 0) {
yield return new Tuple<BigInteger, uint>(first, len);
first += mask;
}
}
}
public static IEnumerable<CIDR> split(IPAddress first, IPAddress last) {
if (first.AddressFamily != last.AddressFamily) {
throw new ArgumentException("AddressFamilies don't match");
}
AddressFamily family = first.AddressFamily;
uint bits = AddressFamilyBits(family); /* split on numbers returns host length, CIDR takes network length */
foreach (Tuple<BigInteger, uint> slice in split(IPToUnsigned(first), IPToUnsigned(last))) {
yield return new CIDR(IPFromUnsigned(slice.Item1, family), bits - slice.Item2);
}
}
}
}

It is difficult to determine the what exactly is being asked here (the CIDR list you give doesn't seem to correspond with the given input addresses), however the following code will allow you to find the smallest single CIDR that contains the specified start and end addresses.
You need to first convert the start and end IP addresses into 32 bit integers (e.g. 192.168.0.1 becomes 0xc0a80001), then apply the following algorithm:
var startAddr = 0xc0a80001; // 192.168.0.1
var endAddr = 0xc0a800fe; // 192.168.0.254
// Determine all bits that are different between the two IPs
var diffs = startAddr ^ endAddr;
// Now count the number of consecutive zero bits starting at the most significant
var bits = 32;
var mask = 0;
while (diffs != 0)
{
// We keep shifting diffs right until it's zero (i.e. we've shifted all the non-zero bits off)
diffs >>= 1;
// Every time we shift, that's one fewer consecutive zero bits in the prefix
bits--;
// Accumulate a mask which will have zeros in the consecutive zeros of the prefix and ones elsewhere
mask = (mask << 1) | 1;
}
// Construct the root of the range by inverting the mask and ANDing it with the start address
var root = startAddr & ~mask;
// Finally, output the range
Console.WriteLine("{0}.{1}.{2}.{3}/{4}", root >> 24, (root >> 16) & 0xff, (root >> 8) & 0xff, root & 0xff, bits);
Running it on the two addresses in your question gives:
192.168.0.0/24

Necromancing.
No, there wasn't, and I don't understand why people keep upvoting wrong answers.
Here's the code for IP-range to CIDR & vice-versa:
// https://dev.maxmind.com/geoip/
// https://stackoverflow.com/questions/461742/how-to-convert-an-ipv4-address-into-a-integer-in-c
public static string IPrange2CIDR(string ip1, string ip2)
{
uint startAddr = IP2num(ip1);
uint endAddr = IP2num(ip2);
// uint startAddr = 0xc0a80001; // 192.168.0.1
// uint endAddr = 0xc0a800fe; // 192.168.0.254
// uint startAddr = System.BitConverter.ToUInt32(System.Net.IPAddress.Parse(ip1).GetAddressBytes(), 0);
// uint endAddr = System.BitConverter.ToUInt32(System.Net.IPAddress.Parse(ip2).GetAddressBytes(), 0);
if (startAddr > endAddr)
{
uint temp = startAddr;
startAddr = endAddr;
endAddr = temp;
}
// uint diff = endAddr - startAddr -1;
// int bits = 32 - (int)System.Math.Ceiling(System.Math.Log10(diff) / System.Math.Log10(2));
// return ip1 + "/" + bits;
uint diffs = startAddr ^ endAddr;
// Now count the number of consecutive zero bits starting at the most significant
int bits = 32;
// int mask = 0;
// We keep shifting diffs right until it's zero (i.e. we've shifted all the non-zero bits off)
while (diffs != 0)
{
diffs >>= 1;
bits--; // Every time we shift, that's one fewer consecutive zero bits in the prefix
// Accumulate a mask which will have zeros in the consecutive zeros of the prefix and ones elsewhere
// mask = (mask << 1) | 1;
}
string res = ip1 + "/" + bits;
System.Console.WriteLine(res);
return res;
}
// https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking
public static void CIDR2IP(string IP)
{
string[] parts = IP.Split('.', '/');
uint ipnum = (System.Convert.ToUInt32(parts[0]) << 24) |
(System.Convert.ToUInt32(parts[1]) << 16) |
(System.Convert.ToUInt32(parts[2]) << 8) |
System.Convert.ToUInt32(parts[3]);
int maskbits = System.Convert.ToInt32(parts[4]);
uint mask = 0xffffffff;
mask <<= (32 - maskbits);
uint ipstart = ipnum & mask;
uint ipend = ipnum | (mask ^ 0xffffffff);
string fromRange = string.Format("{0}.{1}.{2}.{3}", ipstart >> 24, (ipstart >> 16) & 0xff, (ipstart >> 8) & 0xff, ipstart & 0xff);
string toRange = string.Format("{0}.{1}.{2}.{3}", ipend >> 24, (ipend >> 16) & 0xff, (ipend >> 8) & 0xff, ipend & 0xff);
System.Console.WriteLine(fromRange + " - " + toRange);
}
public static uint IP2num(string ip)
{
string[] nums = ip.Split('.');
uint first = System.UInt32.Parse(nums[0]);
uint second = System.UInt32.Parse(nums[1]);
uint third = System.UInt32.Parse(nums[2]);
uint fourth = System.UInt32.Parse(nums[3]);
return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
public static void Test()
{
string IP = "5.39.40.96/27";
// IP = "88.84.128.0/19";
CIDR2IP(IP);
// IPrange2CIDR("88.84.128.0", "88.84.159.255");
IPrange2CIDR("5.39.40.96", "5.39.40.127");
System.Console.WriteLine(System.Environment.NewLine);
System.Console.WriteLine(" --- Press any key to continue --- ");
System.Console.ReadKey();
}

I use this for IpV4, let me know if there is a problem in it.
You can find the extraction source code from the following link:
https://blog.ip2location.com/knowledge-base/how-to-convert-ip-address-range-into-cidr/
using System;
using System.Collections.Generic;
using System.Net;
namespace ConsoleApp
{
public class IPNetwork
{
private readonly long _firstIpAddress;
private readonly long _lastIpAddress;
public static IPNetwork[] FromIpRange(IPAddress firstIpAddress, IPAddress lastIpAddress)
=> FromIpRange(IpAddressToLong(firstIpAddress), IpAddressToLong(lastIpAddress));
public static IPNetwork[] FromIpRange(long firstIpAddress, long lastIpAddress)
{
var result = new List<IPNetwork>();
while (lastIpAddress >= firstIpAddress)
{
byte maxSize = 32;
while (maxSize > 0)
{
long mask = IMask(maxSize - 1);
long maskBase = firstIpAddress & mask;
if (maskBase != firstIpAddress)
break;
maxSize--;
}
double x = Math.Log(lastIpAddress - firstIpAddress + 1) / Math.Log(2);
byte maxDiff = (byte)(32 - Math.Floor(x));
if (maxSize < maxDiff)
{
maxSize = maxDiff;
}
var ipAddress = IpAddressFromLong(firstIpAddress);
result.Add(new IPNetwork(ipAddress, maxSize));
firstIpAddress += (long)Math.Pow(2, 32 - maxSize);
}
return result.ToArray();
}
private static long IMask(int s)
{
return (long)(Math.Pow(2, 32) - Math.Pow(2, 32 - s));
}
public static long IpAddressToLong(IPAddress ipAddress)
{
var bytes = ipAddress.GetAddressBytes();
return ((long)bytes[0] << 24) | ((long)bytes[1] << 16) | ((long)bytes[2] << 8) | bytes[3];
}
public static IPAddress IpAddressFromLong(long ipAddress)
=> new IPAddress((uint)IPAddress.NetworkToHostOrder((int)ipAddress));
public IPNetwork(IPAddress prefix, int prefixLength = 32)
{
if (prefix.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
throw new NotSupportedException("IPv6 is not supported");
Prefix = prefix;
PrefixLength = prefixLength;
var mask = (uint)~(0xFFFFFFFFL >> prefixLength);
_firstIpAddress = IpAddressToLong(Prefix) & mask;
_lastIpAddress = _firstIpAddress | ~mask;
}
public static IPNetwork Parse(string value)
{
try
{
var parts = value.Split('/');
return new IPNetwork(IPAddress.Parse(parts[0]), int.Parse(parts[1]));
}
catch
{
throw new FormatException($"Could not parse IPNetwork from {value}");
}
}
public override string ToString() => $"{Prefix}/{PrefixLength}";
public IPAddress Prefix { get; }
public int PrefixLength { get; }
public IPAddress LastAddress => IpAddressFromLong(_lastIpAddress);
public IPAddress FirstAddress => IpAddressFromLong(_firstIpAddress);
public long Total => _lastIpAddress - _firstIpAddress + 1;
}
}
Usage 1:
var startAddress = IPAddress.Parse("192.168.0.0");
var endAddress = IPAddress.Parse("192.168.0.255");
foreach (var item in IPNetwork.FromIpRange(startAddress, endAddress))
Console.WriteLine(item);
Result
192.168.0.0/24
Usage 2:
var startAddress = IPAddress.Parse("192.168.0.1");
var endAddress = IPAddress.Parse("192.168.0.254");
foreach (var item in IPNetwork.FromIpRange(startAddress, endAddress))
Console.WriteLine(item);
Result:
192.168.0.1/32
192.168.0.2/31
192.168.0.4/30
192.168.0.8/29
192.168.0.16/28
192.168.0.32/27
192.168.0.64/26
192.168.0.128/26
192.168.0.192/27
192.168.0.224/28
192.168.0.240/29
192.168.0.248/30
192.168.0.252/31
192.168.0.254/32

I would recommend the use of IPNetwork Library https://github.com/lduchosal/ipnetwork.
As of version 2, it supports IPv4 and IPv6 as well.
Supernet
IPNetwork network = IPNetwork.Parse("192.168.0.1");
IPNetwork network2 = IPNetwork.Parse("192.168.0.254");
IPNetwork ipnetwork = IPNetwork.Supernet(network, network2);
Console.WriteLine("Network : {0}", ipnetwork.Network);
Console.WriteLine("Netmask : {0}", ipnetwork.Netmask);
Console.WriteLine("Broadcast : {0}", ipnetwork.Broadcast);
Console.WriteLine("FirstUsable : {0}", ipnetwork.FirstUsable);
Console.WriteLine("LastUsable : {0}", ipnetwork.LastUsable);
Console.WriteLine("Usable : {0}", ipnetwork.Usable);
Console.WriteLine("Cidr : {0}", ipnetwork.Cidr);
Output
Network : 192.168.0.0
Netmask : 255.255.255.0
Broadcast : 192.168.0.255
FirstUsable : 192.168.0.1
LastUsable : 192.168.0.254
Usable : 254
Cidr : 24
Have fun !

I found this C code and converted it into C# and it's working now.

Related

Fast byte copy C++11

I need to convert C# app which uses extensively bytes manipulation.
An example:
public abstract class BinRecord
{
public static int version => 1;
public virtual int LENGTH => 1 + 7 + 8 + 2 + 1; // 19
public char type;
public ulong timestamp; // 7 byte
public double p;
public ushort size;
public char callbackType;
public virtual void FillBytes(byte[] bytes)
{
bytes[0] = (byte)type;
var t = BitConverter.GetBytes(timestamp);
Buffer.BlockCopy(t, 0, bytes, 1, 7);
Buffer.BlockCopy(BitConverter.GetBytes(p), 0, bytes, 8, 8);
Buffer.BlockCopy(BitConverter.GetBytes(size), 0, bytes, 16, 2);
bytes[18] = (byte)callbackType;
}
}
Basically BitConverter and Buffer.BlockCopy called 100s times per sec.
There are several classes that inherit from the base class above doing more specific tasks. For example:
public class SpecRecord : BinRecord
{
public override int LENGTH => base.LENGTH + 2;
public ushort num;
public SpecRecord() { }
public SpecRecord(ushort num)
{
this.num = num;
}
public override void FillBytes(byte[] bytes)
{
var idx = base.LENGTH;
base.FillBytes(bytes);
Buffer.BlockCopy(BitConverter.GetBytes(num), 0, bytes, idx + 0, 2);
}
}
What approach in C++ should I look into?
Best option, in my opinion, is to actually go to C - use memcpy to copy over the bytes of any object.
Your above code would then be re-written as follows:
void FillBytes(uint8_t* bytes)
{
bytes[0] = (uint8_t)type;
memcpy((bytes + 1), &t, sizeof(uint64_t) - 1);
memcpy((bytes + 8), &p, sizeof(double));
memcpy((bytes + 16), &size, sizeof(uint16_t));
bytes[18] = (uint8_t)callbackType;
}
Here, I use uint8_t, uint16_t, and uint64_t as replacements for the byte, ushort, and ulong types.
Keep in mind, your timestamp copy is not portable to a big-endian CPU - it will cut off the lowest byte rather than the highest. Solving that would require copying in each byte manually, like so:
//Copy a 7 byte timestamp into the buffer.
bytes[1] = (t >> 0) & 0xFF;
bytes[2] = (t >> 8) & 0xFF;
bytes[3] = (t >> 16) & 0xFF;
bytes[4] = (t >> 24) & 0xFF;
bytes[5] = (t >> 32) & 0xFF;
bytes[6] = (t >> 40) & 0xFF;
bytes[7] = (t >> 48) & 0xFF;

UUID V1 conversion from PHP to C# resulted wrong GUID

I am not familiar with PHP code. I want to convert PHP code implemented UUID V1 to C#. I have tried many ways but it fails. Which part of code is wrong?
This C# code produces wrong GUID fa570fa10-3b235-472b-500-1ebc212c87e0 with node parameter of 138417599493834080 (the result can be vary depends on unix date time). When I change method Hex2Dec as written here it produces 393031383131343830-3234313135-3138323139-313238-1ebc212c87e0. I have no more idea about the wrong code. Please help me to solve it.
public static function v1($node)
{
// nano second time (only micro second precision) since start of UTC
$time = microtime(true) * 10000000 + 0x01b21dd213814000;
$time = pack("H*", sprintf('%016x', $time));
$sequence = random_bytes(2);
$sequence[0] = chr(ord($sequence[0]) & 0x3f | 0x80); // variant bits 10x
$time[0] = chr(ord($time[0]) & 0x0f | 0x10); // version bits 0001
if (!empty($node)) {
// non hex string identifier
if (is_string($node) && preg_match('/[^a-f0-9]/is', $node)) {
// base node off md5 hash for sequence
$node = md5($node);
// set multicast bit not IEEE 802 MAC
$node = (hexdec(substr($node, 0, 2)) | 1) . substr($node, 2, 10);
}
if (is_numeric($node))
$node = sprintf('%012x', $node);
$len = strlen($node);
if ($len > 12)
$node = substr($node, 0, 12);
else if ($len < 12)
$node .= str_repeat('0', 12 - $len);
} else {
// base node off random sequence
$node = random_bytes(6);
// set multicast bit not IEEE 802 MAC
$node[0] = chr(ord($node[0]) | 1);
$node = bin2hex($node);
}
return bin2hex($time[4] . $time[5] . $time[6] . $time[7]) // time low
. '-' . bin2hex($time[2] . $time[3]) // time med
. '-' . bin2hex($time[0] . $time[1]) // time hi
. '-' . bin2hex($sequence) // seq
. '-' . $node; // node
}
This is the C# part
public static string MD5(this string input)
{
// Use input string to calculate MD5 hash
using (System.Security.Cryptography.MD5 crypto = System.Security.Cryptography.MD5.Create())
{
byte[] hashBytes = crypto.ComputeHash(Encoding.ASCII.GetBytes(input));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
sb.Append(hashBytes[i].ToString("x2"));
return sb.ToString();
}
}
public static string GenerateGuidV1(string node)
{
var xtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 10000000 + 0x01b21dd213814000;
var time = Pack(xtime.ToString("x"));
var sequence = new byte[2];
sequence[0] = (byte)((char)sequence[0] & 0x3f | 0x80); // variant bits 10x
time[0] = (byte)((char)time[0] & 0x0f | 0x10); // version bits 0001
if (!string.IsNullOrWhiteSpace(node))
{
// non hex string identifier
if (!IsNumeric(node) && Regex.IsMatch(node, "/[^a-f0-9]/is", RegexOptions.IgnoreCase))
//if (preg_match('/[^a-f0-9]/is', $node))
{
// base node off md5 hash for sequence
//$node = md5($node);
node = node.MD5();
// set multicast bit not IEEE 802 MAC
//$node = (hexdec(substr($node, 0, 2)) | 1) . substr($node, 2, 10);
node = (int.Parse(node.Substring(0, 2), NumberStyles.HexNumber) | 1) + node.Substring(2, 10);
}
if (IsNumeric(node))
node = Convert.ToInt64(node).ToString("x"); //sprintf('%012x', $node);
var len = node.Length;
if (len > 12)
node = node.Substring(0, 12); //substr($node, 0, 12);
else if (len < 12)
node += string.Concat(Enumerable.Repeat("0", 12 - len));//str_repeat('0', 12 - $len);
}
else
{
// base node off random sequence
var seqNode = new byte[6];//$node = random_bytes(6);
// set multicast bit not IEEE 802 MAC
seqNode[0] = (byte)((char)node[0] | 1);//$node[0] = chr(ord($node[0]) | 1);
node = Convert.ToInt16(seqNode[0].ToString(), 2).ToString("x");//bin2hex($node);
}
return Bin2Hex(time[4].ToString() + time[5].ToString() + time[6].ToString() + time[7].ToString()) // time low
+ '-'+ Bin2Hex(time[2].ToString() + time[3].ToString()) // time med
+ '-'+ Bin2Hex(time[0].ToString() + time[1].ToString()) // time hi
+ '-'+ Bin2Hex(sequence[0].ToString() + sequence[1].ToString()) // seq
+ '-'+ node; // node
}
private static string Bin2Hex(string value)
{
return Convert.ToInt64(value).ToString("x");
//byte[] bytes = Encoding.GetEncoding(1252).GetBytes(value);
//string hexString = "";
//for (int ii = 0; ii < bytes.Length; ii++)
//{
// hexString += bytes[ii].ToString("x2");
//}
//return hexString;
}
private static byte[] Pack(string hex)
{
hex = hex.Replace("-", "");
byte[] raw = new byte[hex.Length / 2];
for (int i = 0; i < raw.Length; i++)
{
raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return raw;
}
private static bool IsNumeric(string value) => value.All(char.IsNumber);

How can I get the data of 8bit wav file

I'm making a demo about sound in WindowsForm, I created 3 classes for taking data of wave file. Code is below:
RiffBlock
public class RiffBlock
{
private byte[] riffID;
private uint riffSize;
private byte[] riffFormat;
public byte[] RiffID
{
get { return riffID; }
}
public uint RiffSize
{
get { return (riffSize); }
}
public byte[] RiffFormat
{
get { return riffFormat; }
}
public RiffBlock()
{
riffID = new byte[4];
riffFormat = new byte[4];
}
public void ReadRiff(FileStream inFS)
{
inFS.Read(riffID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
riffSize = binRead.ReadUInt32();
inFS.Read(riffFormat, 0, 4);
}
}
FormatBlock
public class FormatBlock
{
private byte[] fmtID;
private uint fmtSize;
private ushort fmtTag;
private ushort fmtChannels;
private uint fmtSamplesPerSec;
private uint fmtAverageBytesPerSec;
private ushort fmtBlockAlign;
private ushort fmtBitsPerSample;
public byte[] FmtID
{
get { return fmtID; }
}
public uint FmtSize
{
get { return fmtSize; }
}
public ushort FmtTag
{
get { return fmtTag; }
}
public ushort Channels
{
get { return fmtChannels; }
}
public uint SamplesPerSec
{
get { return fmtSamplesPerSec; }
}
public uint AverageBytesPerSec
{
get { return fmtAverageBytesPerSec; }
}
public ushort BlockAlign
{
get { return fmtBlockAlign; }
}
public ushort BitsPerSample
{
get { return fmtBitsPerSample; }
}
public FormatBlock()
{
fmtID = new byte[4];
}
public void ReadFmt(FileStream inFS)
{
inFS.Read(fmtID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
fmtSize = binRead.ReadUInt32();
fmtTag = binRead.ReadUInt16();
fmtChannels = binRead.ReadUInt16();
fmtSamplesPerSec = binRead.ReadUInt32();
fmtAverageBytesPerSec = binRead.ReadUInt32();
fmtBlockAlign = binRead.ReadUInt16();
fmtBitsPerSample = binRead.ReadUInt16();
// This accounts for the variable format header size
// 12 bytes of Riff Header, 4 bytes for FormatId, 4 bytes for FormatSize & the Actual size of the Format Header
inFS.Seek(fmtSize + 20, System.IO.SeekOrigin.Begin);
}
}
DataBlock
public class DataBlock
{
private byte[] dataID;
private uint dataSize;
private Int16[] data;
private int dataNumSamples;
public byte[] DataID
{
get { return dataID; }
}
public uint DataSize
{
get { return dataSize; }
}
public Int16 this[int pos]
{
get { return data[pos]; }
}
public int NumSamples
{
get { return dataNumSamples; }
}
public DataBlock()
{
dataID = new byte[4];
}
public void ReadData(FileStream inFS)
{
inFS.Read(dataID, 0, 4);
BinaryReader binRead = new BinaryReader(inFS);
dataSize = binRead.ReadUInt32();
data = new Int16[dataSize];
inFS.Seek(40, SeekOrigin.Begin);
dataNumSamples = (int)(dataSize / 2);
for (int i = 0; i < dataNumSamples; i++)
{
data[i] = binRead.ReadInt16();
}
}
}
It works ok with only 16bit wave file, but when I choose a 8 bit wav file or another, the result of this command dataSize = binRead.ReadUInt32();is only 4 although the file size is big.
How I can get the data of 8bit, 24bit... wav file?
Some solutions is appreciated, thank you very much.
Your reading methodology is flawed. The length is correct but for an 8 bits per sample file you should be reading bytes not words; as it stands the data will be incorrect and the value returned by the NumSamples property will be wrong.
In my case, the sub chunk size is 1160, the number of channels is 1 (mono) and the bits per sample is 8 (byte). You will need to decode the bits per sample and adjust your reading accordingly. For the WAV file I used, your program allocated a data array the correct length but 16 bit, divided the data length by 2 and called this the number of samples (wrong, it should be 1160) and then proceeded to read 580 word values from the stream.
Edit: My ancient code will not cut it in the modern age (I seem to recall having to modify it some years ago to cope with at least one additional chunk type but the details escape me).
This is what you get; anything more and your question should read "Could someone write me a program to load WAV files", as it is, we are way beyond the original question and it is time for you to knuckle down and make it work how you need it to :-)
References:
http://www.neurophys.wisc.edu/auditory/riff-format.txt
http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
http://www.shikadi.net/moddingwikiResource_Interchange_File_Format_(RIFF)
http://soundfile.sapp.org/doc/WaveFormat/
All are very useful.
///<summary>
///Reads up to 16 bit WAV files
///</summary>
///<remarks> Things have really changed in the last 15 years</remarks>
public class RiffLoader
{
public enum RiffStatus { Unknown = 0, OK, FileError, FormatError, UnsupportedFormat };
LinkedList<Chunk> chunks = new LinkedList<Chunk>();
RiffStatus status = RiffStatus.Unknown;
List<String> errorMessages = new List<string>();
String source;
public String SourceName { get { return source; } }
public RiffStatus Status { get { return status; } }
public String[] Messages { get { return errorMessages.ToArray(); } }
enum chunkType { Unknown = 0, NoMore, Riff, Fmt, Fact, Data, Error = -1 };
static Int32 scan32bits(byte[] source, int offset = 0)
{
return source[offset] | source[offset + 1] << 8 | source[offset + 2] << 16 | source[offset + 3] << 24;
}
static Int32 scan16bits(byte[] source, int offset = 0)
{
return source[offset] | source[offset + 1] << 8;
}
static Int32 scan8bits(byte[] source, int offset = 0)
{
return source[offset];
}
abstract class Chunk
{
public chunkType Ident = chunkType.Unknown;
public int ByteCount = 0;
}
class RiffChunk : Chunk
{
public RiffChunk(int count)
{
this.Ident = chunkType.Riff;
this.ByteCount = count;
}
}
class FmtChunk : Chunk
{
int formatCode = 0;
int channels = 0;
int samplesPerSec = 0;
int avgBytesPerSec = 0;
int blockAlign = 0;
int bitsPerSample = 0;
int significantBits = 0;
public int Format { get { return formatCode; } }
public int Channels { get { return channels; } }
public int BlockAlign { get { return blockAlign; } }
public int BytesPerSample { get { return bitsPerSample / 8 + ((bitsPerSample % 8) > 0 ? 1 : 0); } }
public int BitsPerSample
{
get
{
if (significantBits > 0)
return significantBits;
return bitsPerSample;
}
}
public FmtChunk(byte[] buffer) : base()
{
int size = buffer.Length;
// if the length is 18 then buffer 16,17 should be 00 00 (I don't bother checking)
if (size != 16 && size != 18 && size != 40)
return;
formatCode = scan16bits(buffer, 0);
channels = scan16bits(buffer, 2);
samplesPerSec = scan32bits(buffer, 4);
avgBytesPerSec = scan32bits(buffer, 8);
blockAlign = scan16bits(buffer, 12);
bitsPerSample = scan16bits(buffer, 14);
if (formatCode == 0xfffe) // EXTENSIBLE
{
if (size != 40)
return;
significantBits = scan16bits(buffer, 18);
// skiping speaker map
formatCode = scan16bits(buffer, 24); // first two bytes of the GUID
// the rest of the GUID is fixed, decode it and check it if you wish
}
this.Ident = chunkType.Fmt;
this.ByteCount = size;
}
}
class DataChunk : Chunk
{
byte[] samples = null;
///<summary>
///Create a data chunk
///<para>
///The supplied buffer must be correctly sized with zero offset and must be purely for this class
///</para>
///<summary>
///<param name="buffer">source array</param>
public DataChunk(byte[] buffer)
{
this.Ident = chunkType.Data;
this.ByteCount = buffer.Length;
samples = buffer;
}
public enum SampleStatus { OK, Duff }
public class Samples
{
public SampleStatus Status = SampleStatus.Duff;
public List<int[]> Channels = new List<int[]>();
#if false // debugger helper method
/*
** Change #if false to #if true to include this
** Break at end of GetSamples on "return retval"
** open immediate window and type retval.DumpLast(16)
** look in output window for dump of last 16 entries
*/
public int DumpLast(int count)
{
for (int i = Channels[0].Length - count; i < Channels[0].Length; i++)
Console.WriteLine(String.Format("{0:X4} {1:X4},{2:X4}", i, Channels[0][i], Channels[1][i]));
return 0;
}
#endif
}
/*
** Return the decoded samples
*/
public Samples GetSamples(FmtChunk format)
{
Samples retval = new Samples();
int samplesPerChannel = this.ByteCount / (format.BytesPerSample * format.Channels);
int mask = 0, sign=0;
int [][] samples = new int [format.Channels][];
for (int c = 0; c < format.Channels; c++)
samples[c] = new int[samplesPerChannel];
if (format.BitsPerSample >= 8 && format.BitsPerSample <= 16) // 24+ is left as an excercise
{
sign = (int)Math.Floor(Math.Pow(2, format.BitsPerSample - 1));
mask = (int)Math.Floor(Math.Pow(2, format.BitsPerSample)) - 1;
int offset = 0, index = 0;
int s = 0;
while (index < samplesPerChannel)
{
for (int c = 0; c < format.Channels; c++)
{
switch (format.BytesPerSample)
{
case 1:
s = scan8bits(this.samples, offset) & mask;
break;
case 2:
s = scan16bits(this.samples, offset) & mask;
break;
}
// sign extend the data to Int32
samples[c][index] = s | ((s & sign) != 0 ? ~mask : 0);
offset += format.BytesPerSample;
}
++index;
}
retval.Channels = new List<int[]>(samples);
retval.Status = SampleStatus.OK;
}
return retval;
}
}
class FactChunk : Chunk
{
int samplesPerChannel;
public int SamplesPerChannel { get { return samplesPerChannel; } }
public FactChunk(byte[] buffer)
{
this.Ident = chunkType.Fact;
this.ByteCount = buffer.Length;
if (buffer.Length >= 4)
samplesPerChannel = scan32bits(buffer);
}
}
class DummyChunk : Chunk
{
public DummyChunk(int size, chunkType type = chunkType.Unknown)
{
this.Ident = type;
this.ByteCount = size;
}
}
public RiffLoader(String fileName)
{
source = fileName;
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(fs))
{
Chunk c = getChunk(fs, reader);
if (c.Ident != chunkType.Riff)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": No valid header"));
}
chunks.AddLast(c);
c = getChunk(fs, reader);
if (c.Ident != chunkType.Fmt)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": No format chunk"));
}
chunks.AddLast(c);
/*
** From now on we just keep scanning to the end of the file
*/
while (fs.Position < fs.Length)
{
c = getChunk(fs, reader);
switch (c.Ident)
{
case chunkType.Fact:
case chunkType.Data:
chunks.AddLast(c);
break;
case chunkType.Unknown:
break; // skip it - don't care what it is
}
}
FmtChunk format = null;
foreach (var chunk in chunks)
{
switch(chunk.Ident)
{
case chunkType.Fmt:
format = chunk as FmtChunk;
break;
case chunkType.Data:
if (format != null)
{
DataChunk dc = chunk as DataChunk;
var x = dc.GetSamples(format);
}
break;
}
}
}
}
catch (Exception e)
{
status = RiffStatus.FileError;
errorMessages.Add(String.Format("Error loading \"{0}\": {1}", e.Message));
}
}
/*
** Get a chunk of data from the file - knows nothing of the internal format of the chunk.
*/
Chunk getChunk(FileStream stream, BinaryReader reader)
{
byte[] buffer;
int size;
buffer = reader.ReadBytes(8);
if (buffer.Length == 8)
{
String prefix = new String(Encoding.ASCII.GetChars(buffer, 0, 4));
size = scan32bits(buffer, 4);
if (size + stream.Position <= stream.Length) // skip if there isn't enough data
{
if (String.Compare(prefix, "RIFF") == 0)
{
/*
** only "WAVE" type is acceptable
**
** Don't read size bytes or the entire file will end up in the RIFF chunk
*/
if (size >= 4)
{
buffer = reader.ReadBytes(4);
String ident = new String(Encoding.ASCII.GetChars(buffer, 0, 4));
if (String.CompareOrdinal(ident, "WAVE") == 0)
return new RiffChunk(size - 4);
}
}
else if (String.Compare(prefix, "fmt ") == 0)
{
if (size >= 16)
{
buffer = reader.ReadBytes(size);
if (buffer.Length == size)
return new FmtChunk(buffer);
}
}
else if (String.Compare(prefix, "fact") == 0)
{
if (size >= 4)
{
buffer = reader.ReadBytes(4);
if (buffer.Length == size)
return new FactChunk(buffer);
}
}
else if (String.Compare(prefix, "data") == 0)
{
// assume that there has to be data
if (size > 0)
{
buffer = reader.ReadBytes(size);
if ((size & 1) != 0) // odd length?
{
if (stream.Position < stream.Length)
reader.ReadByte();
else
size = -1; // force an error - there should be a pad byte
}
if (buffer.Length == size)
return new DataChunk(buffer);
}
}
else
{
/*
** there are a number of weird and wonderful block types - assume there has to be data
*/
if (size > 0)
{
buffer = reader.ReadBytes(size);
if ((size & 1) != 0) // odd length?
{
if (stream.Position < stream.Length)
reader.ReadByte();
else
size = -1; // force an error - there should be a pad byte
}
if (buffer.Length == size)
{
DummyChunk skip = new DummyChunk(size);
return skip;
}
}
}
}
}
return new DummyChunk(0, chunkType.Error);
}
}
You will need to add properties as required and code to navigate the returned linked list. Particular properties you may need are the sample rates in the format block, assuming you are going to process the data in some way.
Adding 24 to 32 bits is simple, if you need to go beyond 32 bits you will have to switch to int64's.
I have tested it with some good samples from http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples.html, as well as your 8 bit file, and it decodes OK and seems to have the correct data.
You should be more than capable of making it work now, good luck.
Edit (again, like the proverbial dog...):
Of course, I should have sign extended the value, so DataChunk.GetSamples() now does that. Looking at the Codeproject files (it isn't the greatest code by the way, but the guy does say that he is only just learning C#, so fair play for tackling a graphical user control) it is obvious that the data is signed. It's a shame that he didn't standardise his source to be an array of Int32's, then it wouldn't matter how many bits the WAV was encoded in.
You could use the Naudio library:
https://naudio.codeplex.com/ (take a look at their website, there are quite a lot of tutorials).
Hope this helps :).

C# CRC implementation

I am trying to integrate a Serial-port device into my application, which needs CRC-CCTT validation for the bytes that I send to it.
I'm kinda new into managing byte packets, and need help for this.
It uses this formula for making the CRC calculus:
[CRC-CCITT P(X)= X16 + C12 + C8 + 1]
So for example for the packet: 0xFC 0x05 0x11, the CRC is 0x5627.
Then I send this packet to the device: 0xFC 0x05 0x11 0x27 0x56
Also, packet lenghts will vary from 5 to 255 (including CRC checks bytes)
I don't know how to implement this, so any idea/suggestions will be welcome.
Hope I made myself clear,
Thanks in Advance.
EDIT:
here is the specification of what I need to do:
standard crc-ccitt is x16 + x12 + x5 + 1 I wrote the one # http://www.sanity-free.com/133/crc_16_ccitt_in_csharp.html If I have time I'll see if I can't modify it to run with the x16 + x12 + x8 + 1 poly.
EDIT:
here you go:
public class Crc16CcittKermit {
private static ushort[] table = {
0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
};
public static ushort ComputeChecksum( params byte[] buffer ) {
if ( buffer == null ) throw new ArgumentNullException( );
ushort crc = 0;
for ( int i = 0; i < buffer.Length; ++i ) {
crc = (ushort)( ( crc >> 8 ) ^ table[( crc ^ buffer[i] ) & 0xff] );
}
return crc;
}
public static byte[] ComputeChecksumBytes( params byte[] buffer ) {
return BitConverter.GetBytes( ComputeChecksum( buffer ) );
}
}
sample:
ushort crc = Crc16CcittKermit.ComputeChecksum( 0xFC, 0x05, 0x11 );
byte[] crcBuffer = Crc16CcittKermit.ComputeChecksumBytes( 0xFC, 0x05, 0x11 )
// crc = 0x5627
// crcBuffer = { 0x27, 0x56 }
Have you tried Googling for an example? There are many of them.
Example 1: http://tomkaminski.com/crc32-hashalgorithm-c-net
Example 2: http://www.sanity-free.com/12/crc32_implementation_in_csharp.html
You also have native MD5 support in .Net through System.Security.Cryptography.MD5CryptoServiceProvider.
EDIT:
If you are looking for an 8-bit algorithm: http://www.codeproject.com/KB/cs/csRedundancyChckAlgorithm.aspx
And 16-bit: http://www.sanity-free.com/133/crc_16_ccitt_in_csharp.html
LOL, I've encountered exactly the same STATUS REQUEST sequense, i'm currently developing software to use with CashCode Bill Validator:). Here's the code worked for me, it's CRC16-CCITT with reversed polynomial equals 0x8408 (BDPConstants.Polynomial in the code). That's the code worked for me:
// TableCRC16Size is 256 of course, don't forget to set in somewhere
protected ushort[] TableCRC16 = new ushort[BDPConstants.TableCRC16Size];
protected void InitCRC16Table()
{
for (ushort i = 0; i < BDPConstants.TableCRC16Size; ++i)
{
ushort CRC = 0;
ushort c = i;
for (int j = 0; j < 8; ++j)
{
if (((CRC ^ c) & 0x0001) > 0)
CRC = (ushort)((CRC >> 1) ^ BDPConstants.Polynominal);
else
CRC = (ushort)(CRC >> 1);
c = (ushort)(c >> 1);
}
TableCRC16[i] = CRC;
}
}
protected ushort CalcCRC16(byte[] aData)
{
ushort CRC = 0;
for (int i = 0; i < aData.Length; ++i)
CRC = (ushort)(TableCRC16[(CRC ^ aData[i]) & 0xFF] ^ (CRC >> 8));
return CRC;
}
Initialize the table somewhere (e.g. Form constructor):
InitCRC16Table();
then use it in your code just like that,
You can use List of bytes instead of array, more convinient to pack byte data in the 'packet' for sending
uint CRC = CalcCRC16(byte[] aByte)
// You need to split your CRC in two bytes of course
byte CRCHW = (byte)((CRC) / 256); // that's your 0x56
byte CRCLW = (byte)(CRC); // that's your 0x27
it works and dose not need table:
/// <summary>
/// Gens the CRC16.
/// CRC-1021 = X(16)+x(12)+x(5)+1
/// </summary>
/// <param name="c">The c.</param>
/// <param name="nByte">The n byte.</param>
/// <returns>System.Byte[][].</returns>
public ushort GenCrc16(byte[] c, int nByte)
{
ushort Polynominal = 0x1021;
ushort InitValue = 0x0;
ushort i, j, index = 0;
ushort CRC = InitValue;
ushort Remainder, tmp, short_c;
for (i = 0; i < nByte; i++)
{
short_c = (ushort)(0x00ff & (ushort) c[index]);
tmp = (ushort)((CRC >> 8) ^ short_c);
Remainder = (ushort)(tmp << 8);
for (j = 0; j < 8; j++)
{
if ((Remainder & 0x8000) != 0)
{
Remainder = (ushort)((Remainder << 1) ^ Polynominal);
}
else
{
Remainder = (ushort)(Remainder << 1);
}
}
CRC = (ushort)((CRC << 8) ^ Remainder);
index++;
}
return CRC;
}
You are actually using CRC-XMODEM LSB-reverse (with 0x8408 coefficient). C# code for this calculus is:
public void crc_bytes(int[] int_input)
{
int_array = int_input;
int int_crc = 0x0; // or 0xFFFF;
int int_lsb;
for (int int_i = 0; int_i < int_array.Length; int_i++)
{
int_crc = int_crc ^ int_array[int_i];
for (int int_j = 0; int_j < 8; int_j ++ )
{
int_lsb = int_crc & 0x0001; // Mask of LSB
int_crc = int_crc >> 1;
int_crc = int_crc & 0x7FFF;
if (int_lsb == 1)
int_crc = int_crc ^ 0x8408;
}
}
int_crc_byte_a = int_crc & 0x00FF;
int_crc_byte_b = (int_crc >> 8) & 0x00FF;
}
Read more (or download project):
http://www.cirvirlab.com/index.php/c-sharp-code-examples/141-c-sharp-crc-computation.html

Convert from BitArray to Byte

I have a BitArray with the length of 8, and I need a function to convert it to a byte. How to do it?
Specifically, I need a correct function of ConvertToByte:
BitArray bit = new BitArray(new bool[]
{
false, false, false, false,
false, false, false, true
});
//How to write ConvertToByte
byte myByte = ConvertToByte(bit);
var recoveredBit = new BitArray(new[] { myByte });
Assert.AreEqual(bit, recoveredBit);
This should work:
byte ConvertToByte(BitArray bits)
{
if (bits.Count != 8)
{
throw new ArgumentException("bits");
}
byte[] bytes = new byte[1];
bits.CopyTo(bytes, 0);
return bytes[0];
}
A bit late post, but this works for me:
public static byte[] BitArrayToByteArray(BitArray bits)
{
byte[] ret = new byte[(bits.Length - 1) / 8 + 1];
bits.CopyTo(ret, 0);
return ret;
}
Works with:
string text = "Test";
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(text);
BitArray bits = new BitArray(bytes);
bytes[] bytesBack = BitArrayToByteArray(bits);
string textBack = System.Text.Encoding.ASCII.GetString(bytesBack);
// bytes == bytesBack
// text = textBack
.
A poor man's solution:
protected byte ConvertToByte(BitArray bits)
{
if (bits.Count != 8)
{
throw new ArgumentException("illegal number of bits");
}
byte b = 0;
if (bits.Get(7)) b++;
if (bits.Get(6)) b += 2;
if (bits.Get(5)) b += 4;
if (bits.Get(4)) b += 8;
if (bits.Get(3)) b += 16;
if (bits.Get(2)) b += 32;
if (bits.Get(1)) b += 64;
if (bits.Get(0)) b += 128;
return b;
}
Unfortunately, the BitArray class is partially implemented in .Net Core class (UWP). For example BitArray class is unable to call the CopyTo() and Count() methods. I wrote this extension to fill the gap:
public static IEnumerable<byte> ToBytes(this BitArray bits, bool MSB = false)
{
int bitCount = 7;
int outByte = 0;
foreach (bool bitValue in bits)
{
if (bitValue)
outByte |= MSB ? 1 << bitCount : 1 << (7 - bitCount);
if (bitCount == 0)
{
yield return (byte) outByte;
bitCount = 8;
outByte = 0;
}
bitCount--;
}
// Last partially decoded byte
if (bitCount < 7)
yield return (byte) outByte;
}
The method decodes the BitArray to a byte array using LSB (Less Significant Byte) logic. This is the same logic used by the BitArray class. Calling the method with the MSB parameter set on true will produce a MSB decoded byte sequence. In this case, remember that you maybe also need to reverse the final output byte collection.
This should do the trick. However the previous answer is quite likely the better option.
public byte ConvertToByte(BitArray bits)
{
if (bits.Count > 8)
throw new ArgumentException("ConvertToByte can only work with a BitArray containing a maximum of 8 values");
byte result = 0;
for (byte i = 0; i < bits.Count; i++)
{
if (bits[i])
result |= (byte)(1 << i);
}
return result;
}
In the example you posted the resulting byte will be 0x80. In other words the first value in the BitArray coresponds to the first bit in the returned byte.
That's should be the ultimate one. Works with any length of array.
private List<byte> BoolList2ByteList(List<bool> values)
{
List<byte> ret = new List<byte>();
int count = 0;
byte currentByte = 0;
foreach (bool b in values)
{
if (b) currentByte |= (byte)(1 << count);
count++;
if (count == 7) { ret.Add(currentByte); currentByte = 0; count = 0; };
}
if (count < 7) ret.Add(currentByte);
return ret;
}
In addition to #JonSkeet's answer you can use an Extension Method as below:
public static byte ToByte(this BitArray bits)
{
if (bits.Count != 8)
{
throw new ArgumentException("bits");
}
byte[] bytes = new byte[1];
bits.CopyTo(bytes, 0);
return bytes[0];
}
And use like:
BitArray foo = new BitArray(new bool[]
{
false, false, false, false,false, false, false, true
});
foo.ToByte();
byte GetByte(BitArray input)
{
int len = input.Length;
if (len > 8)
len = 8;
int output = 0;
for (int i = 0; i < len; i++)
if (input.Get(i))
output += (1 << (len - 1 - i)); //this part depends on your system (Big/Little)
//output += (1 << i); //depends on system
return (byte)output;
}
Cheers!
Little endian byte array converter : First bit (indexed with "0") in the BitArray
assumed to represents least significant bit (rightmost bit in the bit-octet) which interpreted as "zero" or "one" as binary.
public static class BitArrayExtender {
public static byte[] ToByteArray( this BitArray bits ) {
const int BYTE = 8;
int length = ( bits.Count / BYTE ) + ( (bits.Count % BYTE == 0) ? 0 : 1 );
var bytes = new byte[ length ];
for ( int i = 0; i < bits.Length; i++ ) {
int bitIndex = i % BYTE;
int byteIndex = i / BYTE;
int mask = (bits[ i ] ? 1 : 0) << bitIndex;
bytes[ byteIndex ] |= (byte)mask;
}//for
return bytes;
}//ToByteArray
}//class

Categories