Binary pattern comparison shortcut / fastest implementation in C# - c#

I need to check a given byte or series of bytes for a particular sequence of bits as follows:
Can start with zero or more number of 0s.
Can start with zero or more number of 1s.
Must contain at least one 0 at the end.
In other words, if the value of bytes is not 0, then we are only interested in values that contain consecutive 1s followed by at least one 0 at the end.
I wrote the following code to do just that but wanted to make sure that it highly optimized. I feel that the multiple checks within the if branches could be optimized but am not sure how. Please advise.
// The parameter [number] will NEVER be negative.
public static bool ConformsToPattern (System.Numerics.BigInteger number)
{
byte [] bytes = null;
bool moreOnesPossible = true;
if (number == 0) // 00000000
{
return (true); // All bits are zero.
}
else
{
bytes = number.ToByteArray();
if ((bytes [bytes.Length - 1] & 1) == 1)
{
return (false);
}
else
{
for (byte b=0; b < bytes.Length; b++)
{
if (moreOnesPossible)
{
if
(
(bytes [b] == 1) // 00000001
|| (bytes [b] == 3) // 00000011
|| (bytes [b] == 7) // 00000111
|| (bytes [b] == 15) // 00001111
|| (bytes [b] == 31) // 00011111
|| (bytes [b] == 63) // 00111111
|| (bytes [b] == 127) // 01111111
|| (bytes [b] == 255) // 11111111
)
{
// So far so good. Continue to the next byte with
// a possibility of more consecutive 1s.
}
else if
(
(bytes [b] == 128) // 10000000
|| (bytes [b] == 192) // 11000000
|| (bytes [b] == 224) // 11100000
|| (bytes [b] == 240) // 11110000
|| (bytes [b] == 248) // 11111000
|| (bytes [b] == 252) // 11111100
|| (bytes [b] == 254) // 11111110
)
{
moreOnesPossible = false;
}
else
{
return (false);
}
}
else
{
if (bytes [b] > 0)
{
return (false);
}
}
}
}
}
return (true);
}
IMPORTANT: The argument [number] sent to the function will NEVER be negative so no need to check for the sign bit.

I'm going to say that none of these answers are accounting for
00000010
00000110
00001110
00011110
00111110
01111110
00000100
00001100
00011100
00111100
01111100
etc, etc, etc.
Here's my byte array method:
public static bool ConformsToPattern(System.Numerics.BigInteger number)
{
bool foundStart = false, foundEnd = false;
int startPosition, stopPosition, increment;
if (number.IsZero || number.IsPowerOfTwo)
return true;
if (!number.IsEven)
return false;
byte[] bytes = number.ToByteArray();
if(BitConverter.IsLittleEndian)
{
startPosition = 0;
stopPosition = bytes.Length;
increment = 1;
}
else
{
startPosition = bytes.Length - 1;
stopPosition = -1;
increment = -1;
}
for(int i = startPosition; i != stopPosition; i += increment)
{
byte n = bytes[i];
for(int shiftCount = 0; shiftCount < 8; shiftCount++)
{
if (!foundEnd)
{
if ((n & 1) == 1)
foundEnd = true;
n = (byte)(n >> 1);
continue;
}
if (!foundStart)
{
if ((n & 1) == 0)
foundStart = true;
n = (byte)(n >> 1);
continue;
}
if (n == 0)
continue;
return false;
}
}
if (foundEnd)
return true;
return false;
}
Here's my BigInteger method:
public static bool ConformsToPattern(System.Numerics.BigInteger number)
{
bool foundStart = false;
bool foundEnd = false;
if (number.IsZero || number.IsPowerOfTwo)
return true;
if (!number.IsEven)
return false;
while (!number.IsZero)
{
if (!foundEnd)
{
if (!number.IsEven)
foundEnd = true;
number = number >> 1;
continue;
}
if (!foundStart)
{
if (number.IsEven)
foundStart = true;
number = number >> 1;
continue;
}
return false;
}
if (foundEnd)
return true;
return false;
}
Choose whichever works better for you. The byte array is faster as of now. The BigIntegers code is 100% accurate reference.
If you're not worried about native endianness remove that part code, but leaving it in there will ensure portability to other than just x86 systems. BigIntegers already gives me IsZero, IsEven and IsPowerOfTwo, so that's not an extra calculation. I'm not sure if that's the fastest way to bitshift right since there is a byte to int cast, but right now, I couldn't find another way. As for use of byte vs short vs int vs long for loop operations, that up to you to change if you feel it'll work better. I'm not sure what kind of BigIntegers you'll be sending so I think int would be safe. You can modify the code to remove the for loop and just copy paste the code 8 times, and it might be faster. Or you can throw that into a static method.

How about something like this? If you find a one, the only things after that can be 1s until a 0 is found. After that, only 0s. This looks like it'll do the trick a little faster because it doesn't do unnecessary or conditions.
// The parameter [number] will NEVER be negative.
public static bool ConformsToPattern (System.Numerics.BigInteger number)
{
byte [] bytes = null;
bool moreOnesPossible = true;
bool foundFirstOne = false;
if (number == 0) // 00000000
{
return (true); // All bits are zero.
}
else
{
bytes = number.ToByteArray();
if ((bytes [bytes.Length - 1] & 1) == 1)
{
return (false);
}
else
{
for (byte b=0; b < bytes.Length; b++)
{
if (moreOnesPossible)
{
if(!foundFirstOne)
{
if
(
(bytes [b] == 1) // 00000001
|| (bytes [b] == 3) // 00000011
|| (bytes [b] == 7) // 00000111
|| (bytes [b] == 15) // 00001111
|| (bytes [b] == 31) // 00011111
|| (bytes [b] == 63) // 00111111
|| (bytes [b] == 127) // 01111111
|| (bytes [b] == 255) // 11111111
)
{
foundFirstOne = true;
// So far so good. Continue to the next byte with
// a possibility of more consecutive 1s.
}
else if
(
(bytes [b] == 128) // 10000000
|| (bytes [b] == 192) // 11000000
|| (bytes [b] == 224) // 11100000
|| (bytes [b] == 240) // 11110000
|| (bytes [b] == 248) // 11111000
|| (bytes [b] == 252) // 11111100
|| (bytes [b] == 254) // 11111110
)
{
moreOnesPossible = false;
}
else
{
return (false);
}
}
else
{
if(bytes [b] != 255) // 11111111
{
if
(
(bytes [b] == 128) // 10000000
|| (bytes [b] == 192) // 11000000
|| (bytes [b] == 224) // 11100000
|| (bytes [b] == 240) // 11110000
|| (bytes [b] == 248) // 11111000
|| (bytes [b] == 252) // 11111100
|| (bytes [b] == 254) // 11111110
)
{
moreOnesPossible = false;
}
}
}
}
else
{
if (bytes [b] > 0)
{
return (false);
}
}
}
}
}
return (true);
}

Here is the method I wrote myself. Not very elegant but pretty fast.
/// <summary>
/// Checks to see if this cell lies on a major diagonal of a power of 2.
/// ^[0]*[1]*[0]+$ denotes the regular expression of the binary pattern we are looking for.
/// </summary>
public bool IsDiagonalMajorToPowerOfTwo ()
{
byte [] bytes = null;
bool moreOnesPossible = true;
System.Numerics.BigInteger number = 0;
number = System.Numerics.BigInteger.Abs(this.X - this.Y);
if ((number == 0) || (number == 1)) // 00000000
{
return (true); // All bits are zero.
}
else
{
// The last bit should always be 0.
if (number.IsEven)
{
bytes = number.ToByteArray();
for (byte b=0; b < bytes.Length; b++)
{
if (moreOnesPossible)
{
switch (bytes [b])
{
case 001: // 00000001
case 003: // 00000011
case 007: // 00000111
case 015: // 00001111
case 031: // 00011111
case 063: // 00111111
case 127: // 01111111
case 255: // 11111111
{
// So far so good.
// Carry on testing subsequent bytes.
break;
}
case 128: // 10000000
case 064: // 01000000
case 032: // 00100000
case 016: // 00010000
case 008: // 00001000
case 004: // 00000100
case 002: // 00000010
case 192: // 11000000
case 096: // 01100000
case 048: // 00110000
case 024: // 00011000
case 012: // 00001100
case 006: // 00000110
case 224: // 11100000
case 112: // 01110000
case 056: // 00111000
case 028: // 00011100
case 014: // 00001110
case 240: // 11110000
case 120: // 01111000
case 060: // 00111100
case 030: // 00011110
case 248: // 11111000
case 124: // 01111100
case 062: // 00111110
case 252: // 11111100
case 126: // 01111110
case 254: // 11111110
{
moreOnesPossible = false;
break;
}
default:
{
return (false);
}
}
}
else
{
if (bytes [b] > 0)
{
return (false);
}
}
}
}
else
{
return (false);
}
}
return (true);
}

If I understand you correctly, you must have only 1 consecutive series of 1's followed by consecutive zeros.
So if it has to end in zero, it has to be even.
All the bytes in the middle must be all 1's and the first and last byte are your only special cases.
if (number.IsZero)
return true;
if (!number.IsEven)
return false;
var bytes = number.ToByteArray();
for (int i = 0; i < bytes.Length; i++)
{
if (i == 0) //first byte case
{
if (!(
(bytes[i] == 1) // 00000001
|| (bytes[i] == 3) // 00000011
|| (bytes[i] == 7) // 00000111
|| (bytes[i] == 15) // 00001111
|| (bytes[i] == 31) // 00011111
|| (bytes[i] == 63) // 00111111
|| (bytes[i] == 127) // 01111111
|| (bytes[i] == 255) // 11111111
))
{
return false;
}
}
else if (i == bytes.Length) //last byte case
{
if (!(
(bytes[i] == 128) // 10000000
|| (bytes[i] == 192) // 11000000
|| (bytes[i] == 224) // 11100000
|| (bytes[i] == 240) // 11110000
|| (bytes[i] == 248) // 11111000
|| (bytes[i] == 252) // 11111100
|| (bytes[i] == 254) // 11111110
))
{
return false;
}
}
else //all bytes in the middle
{
if (bytes[i] != 255)
return false;
}
}

I'm a big fan of regular expressions, so I thought about simply converting the byte to a string and testing it against a regex. However, it's important to carefully define the pattern. By reading your question, I've come up with this one:
^(?:1*)(?:0+)$
Please, check it out:
public static bool ConformsToPattern(System.Numerics.BigInteger number)
{
byte[] ByteArray = number.ToByteArray();
Regex BinaryRegex = new Regex("^(?:1*)(?:0+)$", RegexOptions.Compiled);
return ByteArray.Where<byte>(x => !BinaryRegex.IsMatch(Convert.ToString(x, 2))).Count() > 0;
}

Not sure if this will be faster or slower than what you already have, but it's something to try (hope I didn't botch the logic)...
public bool ConformsToPattern(System.Numerics.BigInteger number) {
bool moreOnesPossible = true;
if (number == 0) {
return true;
}
else {
byte[] bytes = number.ToByteArray();
if ((bytes[bytes.Length - 1] & 1) == 1) {
return false;
}
else {
for (byte b = 0; b < bytes.Length; b++) {
if (moreOnesPossible) {
switch (bytes[b]) {
case 1:
case 3:
case 7:
case 15:
case 31:
case 63:
case 127:
case 255:
continue;
default:
switch (bytes[b]) {
case 128:
case 192:
case 224:
case 240:
case 248:
case 252:
case 254:
moreOnesPossible = false;
continue;
default:
return false;
}
}
}
else {
if (bytes[b] > 0) { return (false); }
}
}
}
}
return true;
}

Related

C# How do I set specific binary bit to 0 or 1?

I've got four checkboxes linked to a hex value between 0x00 and 0x0F, so if you type in 0x0B which would be 1011 the check boxes would be checked, not checked, checked, checked. For this I used:
if ((controlByte.Item2 & 0x01) == 0x01)
{
control1 = true;
}
if ((controlByte.Item2 & 0x02) == 0x02)
{
control2 = true;
}
if ((controlByte.Item2 & 0x04) == 0x04)
{
control3 = true;
}
if ((controlByte.Item2 & 0x08) == 0x08)
{
control4 = true;
}
Next I want it to go the other direction, so as you click checkboxes the hex value will change.
If I have the current value 0x0B (1011) And click the first box I want it to return 0x0A (1010). My function will go over each checkbox and update the hex value as it goes. My current attempt looks like this:
if (checkbox1)
{
controlbyte = (byte)(controlbyte | 0x01);
}
else
{
controlbyte = (byte)(controlbyte ^ 0x01);
}
It looks like this toggles it, but when I run through all checkboxes it toggles ones I didn't click. Is there a better way to take 0x0B (1011) and just set one of the bits to 0 or 1?
( 1 << flag ) <--- change like this;
if ((controlByte.Item2 & (1 << flag)) == (1 << flag))
{
control[flag] = true;
}
//0x01 == 1 << 1;
//0x02 == 1 << 2;
//0x04 == 1 << 3;
//0x08 == 1 << 4;
//because it same like this:
if ((controlByte.Item2 & (1 << 1)) == (1 << 1))
{
control1 = true;
}
if ((controlByte.Item2 & (1 << 2)) == (1 << 2))
{
control2 = true;
}
if ((controlByte.Item2 & (1 << 3)) == (1 << 3))
{
control3 = true;
}
...
controlbyte = (byte)(controlbyte | (1 << 1));
controlbyte = (byte)(controlbyte ^ (1 << 1));

Decode cyrillic quoted-printable content

I'm using this sample for getting mail from server. Problem is that response contains cyrillic symbols I cannot decode.
Here is a header:
Content-type: text/html; charset="koi8-r"
Content-Transfer-Encoding: quoted-printable
And receive response function:
static void receiveResponse(string command)
{
try
{
if (command != "")
{
if (tcpc.Connected)
{
dummy = Encoding.ASCII.GetBytes(command);
ssl.Write(dummy, 0, dummy.Length);
}
else
{
throw new ApplicationException("TCP CONNECTION DISCONNECTED");
}
}
ssl.Flush();
byte[] bigBuffer = new byte[1024*16];
int bites = ssl.Read(bigBuffer, 0, bigBuffer.Length);
byte[] buffer = new byte[bites];
Array.Copy(bigBuffer, 0, buffer, 0, bites);
sb.Append(Encoding.ASCII.GetString(buffer));
string result = sb.ToString();
// here is an unsuccessful attempt at decoding
result = Regex.Replace(result, #"=([0-9a-fA-F]{2})",
m => m.Groups[1].Success
? Convert.ToChar(Convert.ToInt32(m.Groups[1].Value, 16)).ToString()
: "");
byte[] bytes = Encoding.Default.GetBytes(result);
result = Encoding.GetEncoding("koi8r").GetString(bytes);
}
catch (Exception ex)
{
throw new ApplicationException(ex.ToString());
}
}
How to decode stream correctly? In result string I got <p>=F0=D2=C9=D7=C5=D4 =D1 =F7=C1=CE=D1</p> instead of <p>Привет я Ваня</p>.
As #Max pointed out, you will need to decode the content using the encoding algorithm declared in the Content-Transfer-Encoding header.
In your case, it is the quoted-printable encoding.
You will need to decode the text of the message into an array of bytes and then you’ll need to convert that array of bytes into a string using the appropriate System.Text.Encoding. The name of the encoding to use will typically be specified in the Content-Type header as the charset parameter (in your case, koi8-r).
Since you already have the text as bytes in the buffer variable, simply perform the deciding on that:
byte[] buffer = new byte[bites];
int decodedLength = 0;
for (int i = 0; i < bites; i++) {
if (bigBuffer[i] == (byte) '=') {
if (bites > i + 1) {
// possible hex sequence
byte b1 = bigBuffer[i + 1];
byte b2 = bigBuffer[i + 2];
if (IsXDigit (b1) && IsXDigit (b2)) {
// decode
buffer[decodedLength++] = (ToXDigit (b1) << 4) | ToXDigit (b2);
i += 2;
} else if (b1 == (byte) '\r' && b2 == (byte) '\n') {
// folded line, drop the '=\r\n' sequence
i += 2;
} else {
// error condition, just pass it through
buffer[decodedLength++] = bigBuffer[i];
}
} else {
// truncated? just pass it through
buffer[decodedLength++] = bigBuffer[i];
}
} else {
buffer[decodedLength++] = bigBuffer[i];
}
}
string result = Encoding.GetEncoding ("koi8-r").GetString (buffer, 0, decodedLength);
Custom functions:
static byte ToXDigit (byte c)
{
if (c >= 0x41) {
if (c >= 0x61)
return (byte) (c - (0x61 - 0x0a));
return (byte) (c - (0x41 - 0x0A));
}
return (byte) (c - 0x30);
}
static bool IsXDigit (byte c)
{
return (c >= (byte) 'A' && c <= (byte) 'F') || (c >= (byte) 'a' && c <= (byte) 'f') || (c >= (byte) '0' && c <= (byte) '9');
}
Of course, instead of writing your own hodge podge IMAP library, you could just use MimeKit and MailKit ;-)

Encoding errors in embedded Json file

I have run into an issue and can't quite get my head around it.
I have this code:
public List<NavigationModul> LoadNavigation()
{
byte[] navBytes = NavigationResources.Navigation;
var encoding = GetEncoding(navBytes);
string json = encoding.GetString(navBytes);
List<NavigationModul> navigation = JsonConvert.DeserializeObject<List<NavigationModul>>(json);
return navigation;
}
public static Encoding GetEncoding(byte [] textBytes)
{
if (textBytes[0] == 0x2b && textBytes[1] == 0x2f && textBytes[2] == 0x76) return Encoding.UTF7;
if (textBytes[0] == 0xef && textBytes[1] == 0xbb && textBytes[2] == 0xbf) return Encoding.UTF8;
if (textBytes[0] == 0xff && textBytes[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
if (textBytes[0] == 0xfe && textBytes[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
if (textBytes[0] == 0 && textBytes[1] == 0 && textBytes[2] == 0xfe && textBytes[3] == 0xff) return Encoding.UTF32;
return Encoding.ASCII;
}
The Goal is to load an embedded Json File (NavigationResources.Navigation) from a ResourceFile. The Navigation File is an embedded file. We are just jusing the ResourceManager to avoid Magic strings.
After loading the bytes of the embedded file and checking for its encoding, I now read the String from the file and pass it to the JsonConverter.DeserializeObject function.
But unfortunaly this fails due to invalid Json. Long story short: The loaded json string still contains encoding identification bytes. And I can't figure out how to get rid of it.
I also tryed to convert the utf8 bytearray to default encoding before loading the string but this only makes the encoding bytes become a visible charecter.
I talked to my peers and they told me that they have run into the same problem reading embedded batchfiles, leading to broken batchfiles. They didn't know how to fix the problem either, but came up with a workaround for the batchfiles itself (add a blank line into the batchfile to make it work)
Any suggestions on how to fix this?
Thanks to Alex K. I have a solution:
Cuting of the Identification Bytes before calling Encoding.GetString did the trick.
Here is my function I now use to do the Task:
public static string GetStringFromEncodedBytes(byte[] bytes) {
Encoding encoding = Encoding.Default;
int skipBytes = 0;
if (bytes[0] == 0x2b && bytes[1] == 0x2f && bytes[2] == 0x76) {
encoding = Encoding.UTF7;
skipBytes = 3;
}
if (bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf)
{
encoding = Encoding.UTF8;
skipBytes = 3;
}
if (bytes[0] == 0xff && bytes[1] == 0xfe)
{
encoding = Encoding.Unicode;
skipBytes = 2;
}
if (bytes[0] == 0xfe && bytes[1] == 0xff)
{
encoding = Encoding.BigEndianUnicode;
skipBytes = 2;
}
if (bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0xfe && bytes[3] == 0xff)
{
encoding = Encoding.UTF32;
skipBytes = 4;
}
return encoding.GetString(bytes.Skip(skipBytes).ToArray());
}
Here's a simpler approach, removing the BOM after decoding:
// Your data is always in UTF-8 apparently, so just rely on that.
string text = Encoding.UTF8.GetString(data);
if (text.StartsWith("\ufeff"))
{
text = text.Substring(1);
}
This has the downside of copying the string, of course.
Or if you do want to skip the bytes:
// Again, we're assuming UTF-8
int start = data.Length >= 3 && data[0] == 0xef &&
data[1] == 0xbb && data[2] == 0xbf)
? 3 : 0;
string text = Encoding.UTF8.GetString(data, start, data.Length - start);
That way you don't need to use Skip and ToArray, and it avoids doing any extraneous copying.

Checking if Bytes are 0x00

What is the most readable (and idiomatic) to write this method?
private bool BytesAreValid(byte[] bytes) {
var t = (bytes[0] | bytes[1] | bytes[2]);
return t != 0;
}
I need a function which tests the first three bytes of a file that it's not begin with 00 00 00.
Haven't done much byte manipulation. The code above doesn't seem correct to me, since t is inferred of type Int32.
t is type-inferred to be an Int32
Yup, because the | operator (like most operators) isn't defined for byte - the bytes are promoted to int values. (See section 7.11.1 of the C# 4 spec for details.)
But given that you only want to compare it with 0, that's fine anyway.
Personally I'd just write it as:
return bytes[0] != 0 && bytes[1] != 0 && bytes[2] != 0;
Or even:
return (bytes[0] != 0) && (bytes[1] != 0) && (bytes[2] != 0);
Both of these seem clearer to me.
private bool BytesAreValid(byte[] bytes) {
return !bytes.Take(3).SequenceEqual(new byte[] { 0, 0, 0 });
}
To anticipate variable array lengths and avoid null reference exceptions:
private bool BytesAreValid(byte[] bytes)
{
if (bytes == null) return false;
return !Array.Exists(bytes, x => x == 0);
}
Non-Linq version:
private bool BytesAreValid(byte[] bytes)
{
if (bytes == null) return false;
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i] == 0) return false;
}
return true;
}

Given an IP address and subnetmask, how do I calculate the CIDR?

Ok I can't seem to figure this out: given the following:
IP address = 192.168.1.0
Subnetmask = 255.255.255.240
Using c#, how do I calculate the CIDR notation 192.168.1.0/28 ? Is there an easy way to achieve this? Am I missing something?
Thanks!
256 - 240 = 16 = 2**4, 32 - 4 = 28
It is not really a C# question.
To get a net address from an IP and mask you can apply bytewise and to the IP and mask. You can get bytes from a string using IPAddress.Parse() and IPAddress.GetAddressBytes().
I had to do the same thing, no new info but this snippet may come in handy for the next person looking for a way to do this in C#.
note that this method only counts the number of consecutive 1s, and leaves you the work of appending it to the IP.
public class IPAddressHelper
{
public static UInt32 SubnetToCIDR(string subnetStr)
{
IPAddress subnetAddress = IPAddress.Parse(subnetStr);
byte[] ipParts = subnetAddress.GetAddressBytes();
UInt32 subnet = 16777216 * Convert.ToUInt32(ipParts[0]) + 65536 * Convert.ToUInt32(ipParts[1]) + 256 * Convert.ToUInt32(ipParts[2]) + Convert.ToUInt32(ipParts[3]);
UInt32 mask = 0x80000000;
UInt32 subnetConsecutiveOnes = 0;
for (int i = 0; i < 32; i++)
{
if (!(mask & subnet).Equals(mask)) break;
subnetConsecutiveOnes++;
mask = mask >> 1;
}
return subnetConsecutiveOnes;
}
}
Keep it simple!
This works for IPv4 only, but since IPv6 does only support CIDR like /64 in fe80::1ff:fe23:4567:890a/64 calculations like that are unnecessary there.
All you need for an IPv4 network mask:
int cidr = Convert.ToString(mask.Address, 2).Count( o => o == '1');
Explanation based on the given example:
IPAddress mask = new IPAddress(new byte[] { 255, 255, 255, 240 });
// maskBinAsString = 11110000111101001111111111111111
string maskBinAsString = Convert.ToString(mask.Address, 2);
// cidr = 28
int cidr = Convert.ToString(mask.Address, 2).Count( o=> o == '1');
I don't have it as C# code but here is the answer in VB. Should not be to hard to convert.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim someIP As Net.IPAddress = Net.IPAddress.Parse("192.168.1.10")
Dim someMASK As Net.IPAddress = Net.IPAddress.Parse("255.255.255.240")
Dim ipL As Long = IPtoLong(someIP)
Dim maskL As Long = IPtoLong(someMASK)
'Convert Mask to CIDR(1-30)
Dim oneBit As Long = &H80000000L
Dim CIDR As Integer = 0
For x As Integer = 31 To 0 Step -1
If (maskL And oneBit) = oneBit Then CIDR += 1 Else Exit For
oneBit = oneBit >> 1
Next
Dim answer As String = LongToIp(ipL And maskL) & " /" & CIDR.ToString
End Sub
Public Function IPtoLong(ByVal theIP As Net.IPAddress) As Long 'convert IP to number
Dim IPb() As Byte = theIP.GetAddressBytes 'get the octets
Dim addr As Long 'accumulator for address
For x As Integer = 0 To 3
addr = addr Or (CLng(IPb(x)) << (3 - x) * 8)
Next
Return addr
End Function
Public Function LongToIp(ByVal theIP As Long) As String 'convert number back to IP
Dim IPb(3) As Byte '4 octets
Dim addr As String = "" 'accumulator for address
Dim mask8 As Long = MaskFromCidr(8) 'create eight bit mask
For x = 0 To 3 'get the octets
IPb(x) = CByte((theIP And mask8) >> ((3 - x) * 8))
mask8 = mask8 >> 8
addr &= IPb(x).ToString & "." 'add current octet to string
Next
Return addr.TrimEnd("."c)
End Function
Private Function MaskFromCidr(ByVal CIDR As Integer) As Long
MaskFromCidr = CLng(2 ^ ((32 - CIDR)) - 1) Xor 4294967295L
End Function
my solution, first parse to IPAddress:
var Subnetmask = "255.255.255.240";
IPAddress ip = IPAddress.Parse(Subnetmask);
then, check count of set bits in the mask ip:
var intAddress = (int)IPAddress.Address;
Console.WriteLine(NumberOfSetBits(intAddress)); //28
the function (from https://stackoverflow.com/a/12175897/1271037):
int NumberOfSetBits(int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
I know it's a little late, but I took a swing converting dbasnett's answer VB.NET code above to C# (thanks to Telerik Code Converter) and put it up in a .NET Fiddle here and included my conversion below:
public static void Main()
{
System.Net.IPAddress someIP = System.Net.IPAddress.Parse("192.168.1.23");
System.Net.IPAddress someMASK = System.Net.IPAddress.Parse("255.255.255.128");
long ipL = IPtoLong(someIP);
long maskL = IPtoLong(someMASK);
// Convert Mask to CIDR(1-30)
long oneBit = 0x80000000L;
int CIDR = 0;
for (int x = 31; x >= 0; x += -1)
{
if ((maskL & oneBit) == oneBit)
CIDR += 1;
else
break;
oneBit = oneBit >> 1;
}
string answer = LongToIp(ipL & maskL) + "/" + CIDR.ToString();
Console.Out.WriteLine("woah woah we woah " + answer);
}
public static long IPtoLong(System.Net.IPAddress theIP) // convert IP to number
{
byte[] IPb = theIP.GetAddressBytes(); // get the octets
long addr = 0; // accumulator for address
for (int x = 0; x <= 3; x++) {
addr |= (System.Convert.ToInt64(IPb[x]) << (3 - x) * 8);
}
return addr;
}
public static string LongToIp(long theIP) // convert number back to IP
{
byte[] IPb = new byte[4]; // 4 octets
string addr = ""; // accumulator for address
long mask8 = MaskFromCidr(8); // create eight bit mask
for (var x = 0; x <= 3; x++) // get the octets
{
IPb[x] = System.Convert.ToByte((theIP & mask8) >> ((3 - x) * 8));
mask8 = mask8 >> 8;
addr += IPb[x].ToString() + "."; // add current octet to string
}
return addr.TrimEnd('.');
}
private static long MaskFromCidr(int CIDR)
{
return System.Convert.ToInt64(Math.Pow(2, ((32 - CIDR))) - 1) ^ 4294967295L;
}
See Get CIDR from netmask
Usage:
var cidrnetmask = MaskToCIDR(IPAddress.Parse("255.0.0.0").GetAddressBytes());
This works for IPv4. To support IPv6, one could expand the number of bytes but hopefully nobody will try and use old-style netmasks for IPv6 :o)
Method:
static int MaskToCIDR(byte[] bytes)
{
var b0 = bytes[0];
var b1 = bytes[1];
var b2 = bytes[2];
var b3 = bytes[3];
return
b3 != 0 ? (
(b3 & 0x01) != 0 ? 32 :
(b3 & 0x02) != 0 ? 31 :
(b3 & 0x04) != 0 ? 30 :
(b3 & 0x08) != 0 ? 29 :
(b3 & 0x10) != 0 ? 28 :
(b3 & 0x20) != 0 ? 27 :
(b3 & 0x40) != 0 ? 26 :
25 ) :
b2 != 0 ? (
(b2 & 0x01) != 0 ? 24 :
(b2 & 0x02) != 0 ? 23 :
(b2 & 0x04) != 0 ? 22 :
(b2 & 0x08) != 0 ? 21 :
(b2 & 0x10) != 0 ? 20 :
(b2 & 0x20) != 0 ? 19 :
(b2 & 0x40) != 0 ? 18 :
17 ) :
b1 != 0 ? (
(b1 & 0x01) != 0 ? 16 :
(b1 & 0x02) != 0 ? 15 :
(b1 & 0x04) != 0 ? 14 :
(b1 & 0x08) != 0 ? 13 :
(b1 & 0x10) != 0 ? 12 :
(b1 & 0x20) != 0 ? 11 :
(b1 & 0x40) != 0 ? 10 :
9 ) :
b0 != 0 ? (
(b0 & 0x01) != 0 ? 8 :
(b0 & 0x02) != 0 ? 7 :
(b0 & 0x04) != 0 ? 6 :
(b0 & 0x08) != 0 ? 5 :
(b0 & 0x10) != 0 ? 4 :
(b0 & 0x20) != 0 ? 3 :
(b0 & 0x40) != 0 ? 2 :
1 ) :
0;
}

Categories