aelstonjones
aelstonjones

Reputation: 382

c# left-shift nibbles in a byte array

Here is the requirements of my function:

take the following string:

6900460420006149231=13050010300100000

and convert it to the following byte array:

{ 0x37, 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31, 0xD1, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 }

The first byte 0x37 is the original length of the string in binary. The next 10 bytes is the string "6900460420006149231" encoded in bcd format. This is where this gets tricky. Now I need the Hex 'D' to represent the separator(=) between the two fields. You can see the hex in the high nibble of the 12 index in the byte array. The rest of the byte array is the second field "13050010300100000" encoded in bcd format. If the original length is an odd number, I put a leading zero to pad the first half-byte of unused data.

I don't expect a full implementation from anyone so lets break this down and address where I am having trouble. Lets say I have:

byte[] field1Bytes = { 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31 }
byte[] field2Bytes = { 0x01, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 }
byte separator = 13; // D  0x0D

If I simply use Array.Copy, I would end up with:

{ 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31, 0x0D, 0x01, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 }

The above byte array isn't quite what I need.. Any idea on how I could implement the following function to get me closer to what I am trying to achieve:

byte[] ShiftLeftAndCombine(byte[] b1, byte[] b2)

where

ShiftLeftAndCombine({0x0d}, {0x01, 0x30})

would return

{0xd1, 0x30}

Upvotes: 3

Views: 3238

Answers (5)

Monroe Thomas
Monroe Thomas

Reputation: 5042

EDIT: modified to not include the original length in the result.

Hope this helps. Not sure how you calculated the original length to have a value of 0x37, though. :)

Based on the stated desired result, you only need to shift the separator nybble into the first byte of the second array.

byte[] field1Bytes = { 0x06, 0x90, 0x04, 0x60, 0x42, 0x00, 0x06, 0x14, 0x92, 0x31 } ;
byte[] field2Bytes = { 0x01, 0x30, 0x50, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00 } ;
byte separator = 13; // D  0x0D 

byte[] result = new byte[field1Bytes.Length + field2Bytes.Length];


Array.Copy(field1Bytes, 0, result, 0, field1Bytes.Length);
Array.Copy(field2Bytes, 0, result, field1Bytes.Length, field2Bytes.Length);

// shift the separator into the first byte of the second array in the result
result[field1Bytes.Length] |= (byte)(separator << 4);

This produces:

0x06 0x90 0x04 0x60 0x42 0x00 0x06 0x14 0x92 0x31 0xd1 0x30 0x50 0x01 0x03 0x00 0x10 0x00 0x00

... which matches the stated desired result.

Upvotes: 0

David W
David W

Reputation: 10184

What you're really needing to do is shift the entire second block of memory left four bits, seems to me. That means you'll do more than copy, you'll be shifting the "leading" (most significant) bits of byte n+1 into the "trailing" (least significant) nth byte. Here's a generic "bit shifter" for an array of bytes.

    byte[] shiftBlock(byte[] bytes, short bitShift)
    {
        byte[] newBytes = new byte[bytes.Length+1];
        for (int index=0;index < bytes.Length; index++)
        {
            // Each new byte is the current byte shifted left by "bitShift" bits,
            // followed by the first 8-bitShift bits of the next byte OR zero,
            // if we're at the end of the array. Shift the next-bytes bits to
            // the right, and OR the result together. 

            byte newByteMSB = (byte)(bytes[index] << bitShift); // shift left bitShift bits
            byte newByteLSB = (byte)((index==bytes.Length-1)?((byte)0):(bytes[index+1]));
            newByteLSB = (byte) (newByteLSB >> (8-bitShift));

            newBytes[index] = (byte) ( newByteMSB | newByteLSB);

        }

        return newBytes;
    }

You should be able to adapt this into a broader solution with the necessary caveats. I gave it a cursory test and it appears to work on the simple byte arrays I threw at it. Hope this helps!

Upvotes: 1

aelstonjones
aelstonjones

Reputation: 382

Okay I got it:

    static void Main(string[] args)
    {
        const string rawTrack = "6900460420006149231=13050010300100000";

        var byteList = new LinkedList<byte>();

        foreach (var c in rawTrack)
        {
            if(c.Equals('='))
            {
                byteList.AddLast(13);
                continue;
            }

            var bytes = Formatters.Bcd.GetBytes(new string(c, 1));  // for 9 gives 0x09
            byteList.AddLast(bytes[0]);
        }

        // Adjust Buffer if odd length
        if(rawTrack.Length % 2 != 0)
        {
            byteList.AddFirst(0);
        }

        var result = new byte[byteList.Count / 2];
        var buffer = new byte[byteList.Count];
        byteList.CopyTo(buffer, 0);

        var j = 0;
        for(var i = 0; i < buffer.Length - 1; i += 2, j++ )
        {
            result[j] = CombineLowNibble(buffer[i], buffer[i + 1]);
        }


        Console.WriteLine(BitConverter.ToString(result));
    }

    private static byte CombineLowNibble(byte b, byte b1)
    {
        return (byte) ((b << 4) | b1);
    }

Here is the result:

06-90-04-60-42-00-06-14-92-31-D1-30-50-01-03-00-10-00-00

Upvotes: 0

Les
Les

Reputation: 10605

I would make a BCD class. Then BCD.ToByteArray() would give you the current representation of the BCD in Byte[] format, and BCD.ToString() would give the string format. Internally store the BCD as one array element per BCD digit. If you design your own datastructure rather than try to make Byte[] do what it wasn't intended to do, you will be better off.

Upvotes: 0

peacemaker
peacemaker

Reputation: 2591

Not sure if this is homework or not, but the shift left and combine part will look something like this:

var pos = 0;
var newByte = b1[pos] << 4;
newByte |= b2[pos]

Obviously you'll want to do that in a loop and take into account the length of the 2 arrays

Upvotes: 0

Related Questions