tags:

views:

998

answers:

5

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.

+1  A: 

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).

driis
I have to use bytes. To send a UDP packet requires sending a byte[] as the message. Can you please tell me how to express each number as bytes? I understand there will need to be some logic for numbers over 255.Thanks in advance!
If you are _sure_ you never need to send anything above 1024, you can make do with 10 bits per value, which works out to 1250 bytes, which is about 31 % of the naive method of just sending each int as 4 bytes. Would you like an example of that ?
driis
Driis, yes please. However, I need the minimum number of bits per value, and 10 bits is using 2 bytes for each value.
+6  A: 

You need to use :

BitConverter.GetBytes(INTEGER);
Moayad Mardini
I think you are missing the point that he needs to pack the numbers into a minimum number of bytes total.
driis
driis, see Matt's answer below : http://stackoverflow.com/questions/1099691/c-converting-a-sequence-of-numbers-into-bytes/1099915#1099915
Moayad Mardini
Moayad, take a look at my answer :)
Erich Mirabal
A: 

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.

Kevin Montrose
I think this is on the right track, but each number needs to be expressed in the minimum number of bytes. (ie: numbers 1-255 only require 1 byte). Any ideas?
You have to include sentinels if you actually reduce to minimum encoding in the fashion you're looking for. This does not necessarily result in a reduction in the total size. I'll update with a better, but different, approach in a second.
Kevin Montrose
Kevin's solution creates a total stream of 4000 bytes, when I really need a smaller stream. Something like 255 + 2x(1000-255) = 1745 bytes. Any thoughts?
Even if I didn't care about reading the values out of the stream, how would I add the logic such that "i < 256 take a single byte, i < 65535 take two bytes"?
BitConvert has GetBytes methods for all built-in types. Simply cast i to (byte), (UInt16), etc. before calling GetBytes.
Kevin Montrose
Ok so now I have the following. It is still generating 2000 total bytes. I'm not sure I understand casting to UInt16.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();
I updated the code above in the original question so it's easier to read.
+2  A: 

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.

Matt Howells
Sure you can, just use 7-bit encoded integers. It's a little trick used in the BinaryReader/Writers for encoding the length of a string.
Erich Mirabal
I know that, I want the questioner to think about the answer as that is the point of his homework.
Matt Howells
+1  A: 

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).

Erich Mirabal
Erich, how do I use your functions for all the numbers 1-1000?