In C I simply do a memcpy from my data buffer array to the address of my struct.
I am not sure how to do this in C# for the desktop side of things. This is my struct in C#
struct frame_type
{
public UInt32 start_of_frame;
public UInt32 frame_id;
public UInt16 frame_len;
public UInt32 crc;
public UInt32 end_of_frame;
}
And I have a Datareceived serial port callback, the dataIn variable below is a string but obviously I can change it something else to make it easier to grab all those bytes and assemble the frame.
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
dataIN = port.ReadExisting();
//increment variable and when i have 34 bytes assemble a frame
//and checkl if it is an ack frame.
bytes_received_count++;
if(bytes_received_count == 34)
{
//assemble a frame_type frame
}
this.Invoke(new EventHandler(sendFirmware));
}
So any suggestions are welcome.
UPDATE:
I ended up with this code after some research:
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
for (int i = 0; i < 34; i++)
{
bytes_received[i] = (byte)port.ReadByte();
}
assemble_frame_from_port_bytes();
// this.Invoke(new EventHandler(sendFirmware));
}
public void assemble_frame_from_port_bytes()
{
frame.start_of_frame = (UInt32)(bytes_received[3] << 24 | bytes_received[2] << 16 | bytes_received[1] << 8 | bytes_received[0] << 0);
frame.frame_id = (UInt32)(bytes_received[7] << 24 | bytes_received[6] << 16 | bytes_received[5] << 8 | bytes_received[4] << 0);
frame.frame_len = (UInt16)(bytes_received[9] << 8 | bytes_received[8] << 0);
int idx = 10;
for (int i = 0; i < 16; i++)
{
payload[i] = bytes_received[idx++];
}
frame.crc = (UInt32)(bytes_received[29] << 24 | bytes_received[28] << 16 | bytes_received[27] << 8 | bytes_received[26] << 0);
frame.end_of_frame = (UInt32)(bytes_received[33] << 24 | bytes_received[32] << 16 | bytes_received[31] << 8 | bytes_received[30] << 0);
}
It gets the job done, is it great? I dont know but it serves its purpose at this stage. C# surely is a great powerful tool but in the hands of a fool it is crippled haha.
In C I simply do a memcpy from my data buffer array to the address of my struct.
I suppose the following is something similar, but I'm not in a position to test it:
[StructLayout(LayoutKind.Sequential)]
struct frame_type
{
public UInt32 start_of_frame;
public UInt32 frame_id;
public UInt16 frame_len;
public UInt32 crc;
public UInt32 end_of_frame;
public static implicit operator frame_type(byte[] data)
{
unsafe
{
fixed (byte* b = &data[0])
{
return *(frame_type*)b;
}
}
}
}
It should allow you to just straight assign a byte array of length 18 to the struct
frame_type f = aByteArray18Long;
Of course, it is the root of all evil and a safe approach would perhaps look like:
static public implicit operator frame_type(byte[] b)
{
var f = new frame_type();
f.start_of_frame = (uint)(b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]);
f.frame_id = (uint)(b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]);
f.frame_len = (ushort)(b[8]<<8 | b[9]);
f.crc = (uint)(b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13]);
f.end_of_frame = (uint)(b[14]<<24 | b[15]<<16 | b[16]<<8 | b[17]);
return f;
}
ps; it's perhaps easiest to get your bytes by something like:
var buf = new byte[18];
for(int x = 0; x<18; x++)
buf[x] = port.Read();
ok big caveat, I have no serial port so cannot test
MemoryStream buffer = new MemoryStream();
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) {
for (int i = 0; i < port.BytesToRead; i++) {
if (buffer.Length == 34)
break;
// write needs an array even though its only 1
byte[] bytes = new bytes[1];
bytes[0] = port.ReadByte();
buffer.Write(bytes, 0, 1);
}
if (buffer.Length == 34){
// ok we now have the next 34 bytes in buffer
var frame = new frame_type();
// rewind to the beginning
buffer.Seek(0, SeekOrigin.Begin);
using (var br = new BinaryReader(buffer)) {
frame.start_of_frame = br.ReadUInt32();
frame.frame_id = br.ReadUint32();
.......
}
// rewind to be ready for next block
buffer.Seek(0, SeekOrigin.Begin);
}
}
the core is using BinaryReader this reads serialized elements from a byte stream, ReadUInt32 pulls the next 4 bytes and marshalls to a UIn32 etc.
The fiddly bit is getting the bytes from thr port into a stream since we cant connect a stream directly to the port (I coulsnt see how to do it) so I use a MemoryStream which, as the name suggests , is a byte stream in memory
Related
I'm currently working on a master/slave where the Master is a C# program and the Slave is an Arduino Uno. The Arduino is reading several values and is working as expected, but I'm having some troubles on the C# side. I'm reading 3 bytes from an AD converter (AD7680), which returns 3 bytes of data structured in the following way:
0000 | 16 bit number | 0000
My C# program is reading the returned value in a double, which is the expected value. BUT I didn't find out how to get rid of the last four 0's and obtain the 2 byte number I need.
What should be the best approach to get the right value without loosing data? I tried 'BitConverter' but it´s not what I'm expecting, and I have no clue how to proceed. I currently can´t attach the code unfortunately, but I could reference anything on it if needed.
Thanks for reading!
EDIT: This is the function on the C# side:
public double result(byte[] command)
{
try
{
byte[] buffer = command;
arduinoBoard.Open();
arduinoBoard.Write(buffer, 0, 3);
int intReturnASCII = 0;
char charReturnValue = (Char)intReturnASCII;
Thread.Sleep(200);
int count = arduinoBoard.BytesToRead;
double returnResult = 0;
string returnMessage = "";
while (count > 0)
{
intReturnASCII = arduinoBoard.ReadByte();
//string str = char.ConvertFromUtf32(intReturnASCII);
returnMessage = returnMessage + Convert.ToChar(intReturnASCII);
count--;
}
returnResult = double.Parse(returnMessage, System.Globalization.CultureInfo.InvariantCulture);
arduinoBoard.Close();
return returnResult;
}
catch (Exception e)
{
return 0;
}
}
And the Arduino function that communicates with it is this one:
unsigned long ReturnPressure(){
long lBuffer = 0;
byte rtnVal[3];
digitalWrite(SLAVESELECT , LOW);
delayMicroseconds(1);
rtnVal[0] = SPI.transfer(0x00);
delayMicroseconds(1);
rtnVal[1] = SPI.transfer(0x00);
delayMicroseconds(1);
rtnVal[2] = SPI.transfer(0x00);
delayMicroseconds(1);
digitalWrite(SLAVESELECT, HIGH);
// assemble into long type
lBuffer = lBuffer | rtnVal[0];
lBuffer = lBuffer << 8;
lBuffer = lBuffer | rtnVal[1];
lBuffer = lBuffer << 8;
lBuffer = lBuffer | rtnVal[2];
return lBuffer;
}
Okay, you have to do a few steps:
Firstly: Its much easier to save the bytes in an array like this:
byte Received = new byte[3];
for(int i = 0; i < 3; i++)
{
Received[i] = (byte)arduinoBoard.ReadByte();
}
After received the three bytes, shift it together (check if the three bytes are in the right order: Most significant byte is here at index 0)
UInt64 Shifted = (UInt64)(Received[0] << 16) | (UInt64)(Received[1] << 8) | (UInt64)(Received[0])
Now shift out the four ending zeros:
UInt64 Shifted = Shifted >> 4;
To find out, what your voltage is, you have to know the scale of your converter. The Data sheet says, "The LSB size is VDD/65536". You could define a constant
const double VDD = 5; //For example 5V
After that you can calculate your needed double with
return Shifted * (VDD / 65539); //Your voltage
Hope this helps.
I wrote a program that displays live images from a sensor. The problem is that it keeps eating up the memory.
void tmr_Tick(object sender, EventArgs e)
{
object bpic = cam.GrabFrame();
uint[] pic = cam.ColorPipe(bpic, out width, out height, out bitdepth);
byte[] header = GetHeader.BuildHeader(width, height, bitdepth);
byte[] img = GetHeader.UintImageToByte(pic, width, height, bitdepth);
MemoryStream ms = new MemoryStream();
GC.Collect();
ms.Write(header, 0, header.Length);
ms.Write(img, 0, img.Length);
pictureBox1.Image.Dispose();
pictureBox1.Image = Image.FromStream(ms);
ms.Flush();
ms.Dispose();
}
}
class GetHeader
{
#region Image
public static byte[] UintImageToByte(uint[] img, uint nWidth, uint nHeight, uint nBitsPerPixel)
{
int nBytesPerPixel = (int)nBitsPerPixel / 8;
byte[] AoB = new byte[nWidth * nHeight * nBytesPerPixel];
int j = 0;
byte[] pixel;
// Get each uint value from image and convert it to bytes
for (int i = 0; i < img.Length; i++)
{
pixel = BitConverter.GetBytes(img[i]);
// Add each pixel bytes to the image array
for (int k = nBytesPerPixel - 1; k >= 0; k--)
{
AoB[j++] = pixel[k];
}
}
byte temp;
// Swap the elements to create a true color image
for (int i = 0; i < AoB.Length / 2; i++)
{
temp = AoB[i];
AoB[i] = AoB[AoB.Length - 1 - i];
AoB[AoB.Length - 1 - i] = temp;
}
return AoB;
}
#endregion
#region Header
/* IMPORTANT!!!!!
* All numerical values are little endian!
* 4660 in decimal will be 3421 in hex not 1234!
* IMPORTANT!!!!! */
private const uint HEADER_SIZE = 54;
private const string FOUR_BYTE_ZERO = "00000000";
private const string TWO_BYTE_ZERO = "0000";
private const string BM = "424D";
public static byte[] BuildHeader(uint nWidth, uint nHeight, uint nBitsPerPixel)
{
// Substring is to get the amount of bytes needed.
// BitConverter currently returns 4 bytes and in case it will change
// the substring will take the amount of bytes it needs.
string sWidth = BitConverter.ToString(BitConverter.GetBytes(nWidth)).Replace("-", "").Substring(0, 8);
string sHeight = BitConverter.ToString(BitConverter.GetBytes(nHeight)).Replace("-", "").Substring(0, 8);
string sBitsPerPixel = BitConverter.ToString(BitConverter.GetBytes(nBitsPerPixel)).Replace("-", "").Substring(0, 4);
uint nImageSize = ((nWidth * nHeight) * (nBitsPerPixel / 8));
string sFileSize = BitConverter.ToString(BitConverter.GetBytes(nImageSize + HEADER_SIZE)).Replace("-", "").Substring(0, 8);
string sImageSize = BitConverter.ToString(BitConverter.GetBytes(nImageSize)).Replace("-", "").Substring(0, 8);
string sHeader = String.Empty;
sHeader += BM; // 0h | File tpye | 2 bytes
sHeader += sFileSize; // 2h | File size | 4 bytes
sHeader += TWO_BYTE_ZERO; // 6h | N/A | 2 bytes
sHeader += TWO_BYTE_ZERO; // 8h | N/A | 2 bytes
sHeader += "36000000"; // Ah | Data offset | 4 bytes
sHeader += "28000000"; // Eh | DIB Size | 4 bytes
sHeader += sWidth; // 12h | Width | 4 bytes
sHeader += sHeight; // 16h | Height | 4 bytes
sHeader += "0100"; // 1Ah | Color planes | 2 bytes
sHeader += sBitsPerPixel; // 1Ch | Bits per pixel | 2 bytes
sHeader += FOUR_BYTE_ZERO; // 1Eh | Compression | 4 bytes
sHeader += sImageSize; // 22h | Image size | 4 bytes
sHeader += FOUR_BYTE_ZERO; // 26h | Horizontal res | 4 bytes
sHeader += FOUR_BYTE_ZERO; // 2Ah | Vertical res | 4 bytes
sHeader += FOUR_BYTE_ZERO; // 2Eh | Color pallete | 4 bytes
sHeader += FOUR_BYTE_ZERO; // 32h | Important colors | 4 bytes
byte[] Header = StringToAoB(sHeader);
return Header;
}
private static byte[] StringToAoB(string str)
{
byte[] AoB = new byte[str.Length / 2];
for (int i = 0, j = 0; i < AoB.Length; i++, j += 2)
{
AoB[i] = byte.Parse(HexToInt("0x" + str[j] + str[j + 1]).ToString());
}
return AoB;
}
private static int HexToInt(string Hex)
{
Hex = Hex.Replace("0x", "");
return Int32.Parse(Hex, System.Globalization.NumberStyles.AllowHexSpecifier);
}
#endregion
The critical functions are tmr_Tick and UintImageToByte.
cam.GrabFrame() returns a single dimension array containing raw image data.
cam.ColorPipe() arranges it to contain uint representation of each pixel in a single dimension array.
I have no idea on where the problem seems to be, if its memory is not getting cleaned off the memory stream or maybe the interval is too fast (200 ms) for the gc to tag unused memory and clean it up.
I would greatly appreciate if anyone would take some time to look at the code and give some pointers on how it may be fixed.
I've tried using (MemoryStream ms....) block, to no avail.
The ms.Flush(), ms.Dispose() and GC.Collect() are attempts to try and get rid of the used memory, still no go.
Working with a base64 encoding for Azure (http://msdn.microsoft.com/en-us/library/dd135726.aspx) and I dont seem to work out how to get the required string back. I'm able to do this in C# where I do the following.
int blockId = 5000;
var blockIdBytes = BitConverter.GetBytes(blockId);
Console.WriteLine(blockIdBytes);
string blockIdBase64 = Convert.ToBase64String(blockIdBytes);
Console.WriteLine(blockIdBase64);
Which prints out (in LINQPad):
Byte[] (4 items)
| 136 |
| 19 |
| 0 |
| 0 |
iBMAAA==
In Qt/C++ I tried a few aporaches, all of them returning the wrong value.
const int a = 5000;
QByteArray b;
for(int i = 0; i != sizeof(a); ++i) {
b.append((char)(a&(0xFF << i) >>i));
}
qDebug() << b.toBase64(); // "iIiIiA=="
qDebug() << QByteArray::number(a).toBase64(); // "NTAwMA=="
qDebug() << QString::number(a).toUtf8().toBase64(); // "NTAwMA=="
How can I get the same result as the C# version?
See my comment for the problem with your for loop. It's shifting by one bit more each pass, but actually it should be 8 bits. Personally, I prefer this to a loop:
b.append(static_cast<char>(a >> 24));
b.append(static_cast<char>((a >> 16) & 0xff));
b.append(static_cast<char>((a >> 8) & 0xff));
b.append(static_cast<char>(a & 0xff));
The code above is for network standard byte order (big endian). Flip the order of the four operations from last to first for little endian byte order.
I ended up doing the following:
QByteArray temp;
int blockId = 5000;
for(int i = 0; i != sizeof(blockId); i++) {
temp.append((char)(blockId >> (i * 8)));
}
qDebug() << temp.toBase64(); // "iBMAAA==" which is correct
I think this would be clearer, though may be claimed to be ill styled...
int i = 0x01020304;
char (&bytes)[4] = (char (&)[4])i;
and you can access each byte directly with bytes[0], bytes[1], ... and do what ever you want to do with them.
I am making application in C# which has a byte array containing hex values.
I am getting data as a big-endian but I want it as a little-endian and I am using Bitconverter.toInt32 method for converting that value to integer.
My problem is that before converting the value, I have to copy that 4 byte data into temporary array from source byte array and then reverse that temporary byte array.
I can't reverse source array because it also contains other data.
Because of that my application becomes slow.
In the code I have one source array of byte as waveData[] which contains a lot of data.
byte[] tempForTimestamp=new byte[4];
tempForTimestamp[0] = waveData[290];
tempForTimestamp[1] = waveData[289];
tempForTimestamp[2] = waveData[288];
tempForTimestamp[3] = waveData[287];
int number = BitConverter.ToInt32(tempForTimestamp, 0);
Is there any other method for that conversion?
Add a reference to System.Memory nuget and use BinaryPrimitives.ReverseEndianness().
using System.Buffers.Binary;
number = BinaryPrimitives.ReverseEndianness(number);
It supports both signed and unsigned integers (byte/short/int/long).
In modern-day Linq the one-liner and easiest to understand version would be:
int number = BitConverter.ToInt32(waveData.Skip(286).Take(4).Reverse().ToArray(), 0);
You could also...
byte[] tempForTimestamp = new byte[4];
Array.Copy(waveData, 287, tempForTimestamp, 0, 4);
Array.Reverse(tempForTimestamp);
int number = BitConverter.ToInt32(tempForTimestamp);
:)
If you know the data is big-endian, perhaps just do it manually:
int value = (buffer[i++] << 24) | (buffer[i++] << 16)
| (buffer[i++] << 8) | buffer[i++];
this will work reliably on any CPU, too. Note i is your current offset into the buffer.
Another approach would be to shuffle the array:
byte tmp = buffer[i+3];
buffer[i+3] = buffer[i];
buffer[i] = tmp;
tmp = buffer[i+2];
buffer[i+2] = buffer[i+1];
buffer[i+1] = tmp;
int value = BitConverter.ToInt32(buffer, i);
i += 4;
I find the first immensely more readable, and there are no branches / complex code, so it should work pretty fast too. The second could also run into problems on some platforms (where the CPU is already running big-endian).
Here you go
public static int SwapEndianness(int value)
{
var b1 = (value >> 0) & 0xff;
var b2 = (value >> 8) & 0xff;
var b3 = (value >> 16) & 0xff;
var b4 = (value >> 24) & 0xff;
return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
}
Declare this class:
using static System.Net.IPAddress;
namespace BigEndianExtension
{
public static class BigEndian
{
public static short ToBigEndian(this short value) => HostToNetworkOrder(value);
public static int ToBigEndian(this int value) => HostToNetworkOrder(value);
public static long ToBigEndian(this long value) => HostToNetworkOrder(value);
public static short FromBigEndian(this short value) => NetworkToHostOrder(value);
public static int FromBigEndian(this int value) => NetworkToHostOrder(value);
public static long FromBigEndian(this long value) => NetworkToHostOrder(value);
}
}
Example, create a form with a button and a multiline textbox:
using BigEndianExtension;
private void button1_Click(object sender, EventArgs e)
{
short int16 = 0x1234;
int int32 = 0x12345678;
long int64 = 0x123456789abcdef0;
string text = string.Format("LE:{0:X4}\r\nBE:{1:X4}\r\n", int16, int16.ToBigEndian());
text += string.Format("LE:{0:X8}\r\nBE:{1:X8}\r\n", int32, int32.ToBigEndian());
text += string.Format("LE:{0:X16}\r\nBE:{1:X16}\r\n", int64, int64.ToBigEndian());
textBox1.Text = text;
}
//Some code...
The most straightforward way is to use the BinaryPrimitives.ReadInt32BigEndian(ReadOnlySpan) Method introduced in .NET Standard 2.1
var number = BinaryPrimitives.ReadInt32BigEndian(waveData[297..291]);
If you won't ever again need that reversed, temporary array, you could just create it as you pass the parameter, instead of making four assignments. For example:
int i = 287;
int value = BitConverter.ToInt32({
waveData(i + 3),
waveData(i + 2),
waveData(i + 1),
waveData(i)
}, 0);
I use the following helper functions
public static Int16 ToInt16(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian)
return BitConverter.ToInt16(BitConverter.IsLittleEndian ? data.Skip(offset).Take(2).Reverse().ToArray() : data, 0);
return BitConverter.ToInt16(data, offset);
}
public static Int32 ToInt32(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian)
return BitConverter.ToInt32(BitConverter.IsLittleEndian ? data.Skip(offset).Take(4).Reverse().ToArray() : data, 0);
return BitConverter.ToInt32(data, offset);
}
public static Int64 ToInt64(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian)
return BitConverter.ToInt64(BitConverter.IsLittleEndian ? data.Skip(offset).Take(8).Reverse().ToArray() : data, 0);
return BitConverter.ToInt64(data, offset);
}
You can also use Jon Skeet "Misc Utils" library, available at https://jonskeet.uk/csharp/miscutil/
His library has many utility functions. For Big/Little endian conversions you can check the MiscUtil/Conversion/EndianBitConverter.cs file.
var littleEndianBitConverter = new MiscUtil.Conversion.LittleEndianBitConverter();
littleEndianBitConverter.ToInt64(bytes, offset);
var bigEndianBitConverter = new MiscUtil.Conversion.BigEndianBitConverter();
bigEndianBitConverter.ToInt64(bytes, offset);
His software is from 2009 but I guess it's still relevant.
I dislike BitConverter, because (as Marc Gravell answered) it is specced to rely on system endianness, meaning you technically have to do a system endianness check every time you use BitConverter to ensure you don't have to reverse the array. And usually, with saved files, you generally know the endianness you're trying to read, and that might not be the same. You might just be handling file formats with big-endian values, too, like, for instance, PNG chunks.
Because of that, I just wrote my own methods for this, which take a byte array, the read offset and read length as arguments, as well as a boolean to specify the endianness handling, and which uses bit shifting for efficiency:
public static UInt64 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
Int32 lastByte = bytes - 1;
if (data.Length < startIndex + bytes)
throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
UInt64 value = 0;
for (Int32 index = 0; index < bytes; index++)
{
Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
value |= (((UInt64)data[offs]) << (8 * index));
}
return value;
}
This code can handle any value between 1 and 8 bytes, both little-endian and big-endian. The only small usage peculiarity is that you need to both give the amount of bytes to read, and need to specifically cast the result to the type you want.
Example from some code where I used it to read the header of some proprietary image type:
Int16 imageWidth = (Int16) ReadIntFromByteArray(fileData, hdrOffset, 2, true);
Int16 imageHeight = (Int16) ReadIntFromByteArray(fileData, hdrOffset + 2, 2, true);
This will read two consecutive 16-bit integers off an array, as signed little-endian values. You can of course just make a bunch of overload functions for all possibilities, like this:
public Int16 ReadInt16FromByteArrayLe(Byte[] data, Int32 startIndex)
{
return (Int16) ReadIntFromByteArray(data, startIndex, 2, true);
}
But personally I didn't bother with that.
And, here's the same for writing bytes:
public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt64 value)
{
Int32 lastByte = bytes - 1;
if (data.Length < startIndex + bytes)
throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
for (Int32 index = 0; index < bytes; index++)
{
Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
data[offs] = (Byte) (value >> (8*index) & 0xFF);
}
}
The only requirement here is that you have to cast the input arg to 64-bit unsigned integer when passing it to the function.
public static unsafe int Reverse(int value)
{
byte* p = (byte*)&value;
return (*p << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
If unsafe is allowed... Based on Marc Gravell's post
This will reverse the data inline if unsafe code is allowed...
fixed (byte* wavepointer = waveData)
new Span<byte>(wavepointer + offset, 4).Reverse();
I am trying to send a UDP packet of bytes corresponding to the numbers 1-1000 in sequence. How do I convert each number (1,2,3,4,...,998,999,1000) into the minimum number of bytes required and put them in a sequence that I can send as a UDP packet?
I've tried the following with no success. Any help would be greatly appreciated!
List<byte> byteList = new List<byte>();
for (int i = 1; i <= 255; i++)
{
byte[] nByte = BitConverter.GetBytes((byte)i);
foreach (byte b in nByte)
{
byteList.Add(b);
}
}
for (int g = 256; g <= 1000; g++)
{
UInt16 st = Convert.ToUInt16(g);
byte[] xByte = BitConverter.GetBytes(st);
foreach (byte c in xByte)
{
byteList.Add(c);
}
}
byte[] sendMsg = byteList.ToArray();
Thank you.
You need to use :
BitConverter.GetBytes(INTEGER);
Think about how you are going to be able to tell the difference between:
260, 1 -> 0x1, 0x4, 0x1
1, 4, 1 -> 0x1, 0x4, 0x1
If you use one byte for numbers up to 255 and two bytes for the numbers 256-1000, you won't be able to work out at the other end which number corresponds to what.
If you just need to encode them as described without worrying about how they are decoded, it smacks to me of a contrived homework assignment or test, and I'm uninclined to solve it for you.
I think you are looking for something along the lines of a 7-bit encoded integer:
protected void Write7BitEncodedInt(int value)
{
uint num = (uint) value;
while (num >= 0x80)
{
this.Write((byte) (num | 0x80));
num = num >> 7;
}
this.Write((byte) num);
}
(taken from System.IO.BinaryWriter.Write(String)).
The reverse is found in the System.IO.BinaryReader class and looks something like this:
protected internal int Read7BitEncodedInt()
{
byte num3;
int num = 0;
int num2 = 0;
do
{
if (num2 == 0x23)
{
throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32"));
}
num3 = this.ReadByte();
num |= (num3 & 0x7f) << num2;
num2 += 7;
}
while ((num3 & 0x80) != 0);
return num;
}
I do hope this is not homework, even though is really smells like it.
EDIT:
Ok, so to put it all together for you:
using System;
using System.IO;
namespace EncodedNumbers
{
class Program
{
protected static void Write7BitEncodedInt(BinaryWriter bin, int value)
{
uint num = (uint)value;
while (num >= 0x80)
{
bin.Write((byte)(num | 0x80));
num = num >> 7;
}
bin.Write((byte)num);
}
static void Main(string[] args)
{
MemoryStream ms = new MemoryStream();
BinaryWriter bin = new BinaryWriter(ms);
for(int i = 1; i < 1000; i++)
{
Write7BitEncodedInt(bin, i);
}
byte[] data = ms.ToArray();
int size = data.Length;
Console.WriteLine("Total # of Bytes = " + size);
Console.ReadLine();
}
}
}
The total size I get is 1871 bytes for numbers 1-1000.
Btw, could you simply state whether or not this is homework? Obviously, we will still help either way. But we would much rather you try a little harder so you can actually learn for yourself.
EDIT #2:
If you want to just pack them in ignoring the ability to decode them back, you can do something like this:
protected static void WriteMinimumInt(BinaryWriter bin, int value)
{
byte[] bytes = BitConverter.GetBytes(value);
int skip = bytes.Length-1;
while (bytes[skip] == 0)
{
skip--;
}
for (int i = 0; i <= skip; i++)
{
bin.Write(bytes[i]);
}
}
This ignores any bytes that are zero (from MSB to LSB). So for 0-255 it will use one byte.
As states elsewhere, this will not allow you to decode the data back since the stream is now ambiguous. As a side note, this approach crams it down to 1743 bytes (as opposed to 1871 using 7-bit encoding).
A byte can only hold 256 distinct values, so you cannot store the numbers above 255 in one byte. The easiest way would be to use short, which is 16 bits. If you realy need to conserve space, you can use 10 bit numbers and pack that into a byte array ( 10 bits = 2^10 = 1024 possible values).
Naively (also, untested):
List<byte> bytes = new List<byte>();
for (int i = 1; i <= 1000; i++)
{
byte[] nByte = BitConverter.GetBytes(i);
foreach(byte b in nByte) bytes.Add(b);
}
byte[] byteStream = bytes.ToArray();
Will give you a stream of bytes were each group of 4 bytes is a number [1, 1000].
You might be tempted to do some work so that i < 256 take a single byte, i < 65535 take two bytes, etc. However, if you do this you can't read the values out of the stream. Instead, you'd add length encoding or sentinels bits or something of the like.
I'd say, don't. Just compress the stream, either using a built-in class, or gin up a Huffman encoding implementation using an agree'd upon set of frequencies.