开发者

ushort array to byte array

开发者 https://www.devze.com 2023-02-04 16:55 出处:网络
I have an array of ushorts, with each ushort representing a 12-bit word. This needs to be tightly packed into an array of bytes. It should look like this in the end:

I have an array of ushorts, with each ushort representing a 12-bit word. This needs to be tightly packed into an array of bytes. It should look like this in the end:

|  word1    |  word2    |  word3    |  word4   |
| byte1 | byte2 | byte3 | byte4 | byte5 | byte6|

Since each word only uses 12 bits, 2 words will be packed 开发者_运维百科into 3 bytes.

Could someone help? I'm a bit stuck on how to do this in C#.


You're probably going to have to brute-force it.

I'm not a C# guy, but you are looking at something along the lines of (in C):

unsigned incursor, outcursor;
unsigned inlen = length(inputarray);  // not literally
for(incursor=0,outcursor=0;incursor < inlen; incursor+=2,outcursor+=3{
   outputarray[outcursor+0] = ((inputarray[incursor+0]) >> 4) & 0xFF;
   outputarray[outcursor+1] = ((inputarray[incursor+0] & 0x0F)<<4 | ((inputarray[incursor+1]>>8) & 0x0F);
   outputarray[outcursor+2] = inputarray[incursor+1] & 0xFF;
}


If you want to use the array as an array of UInt16 while in-memory, and then convert it to a packed byte array for storage, then you'll want a function to do one-shot conversion of the two array types.

public byte[] PackUInt12(ushort[] input)
{
    byte[] result = new byte[(input.Length * 3 + 1) / 2]; // the +1 leaves space if we have an odd number of UInt12s. It's the unused half byte at the end of the array.
    for(int i = 0; i < input.Length / 2; i++)
    {
        result[i * 3 + 0] = (byte)input[i * 2 + 0];
        result[i * 3 + 1] = (byte)(input[i * 2 + 0] >> 8 | input[i * 2 + 1] << 4);
        result[i * 3 + 2] = (byte)(input[i * 2 + 1] >> 4);
    }
    if(input.Length % 2 == 1)
    {
        result[i * 3 + 0] = (byte)input[i * 2 + 0];
        result[i * 3 + 1] = (byte)(input[i * 2 + 0] >> 8);
    }
    return result;
}

public ushort[] UnpackUInt12(byte[] input)
{
    ushort[] result = new ushort[input.Length * 2 / 3];
    for(int i = 0; i < input.Length / 3; i++)
    {
        result[i * 2 + 0] = (ushort)(((ushort)input[i * 3 + 1]) << 8 & 0x0F00 | input[i * 3 + 0]);
        result[i * 2 + 1] = (ushort)(((ushort)input[i * 3 + 1]) << 4 | input[i * 3 + 1] >> 4;)
    }
    if(result.Length % 2 == 1)
    {
        result[i * 2 + 0] = (ushort)(((ushort)input[i * 3 + 1]) << 8 & 0x0F00 | input[i * 3 + 0]);
    }
    return result;
}

If, however, you want to be efficient about memory usage while the application is running, and access this packed array as an array, then you'll want to have a class that returns ushorts, but stores them in byte[].

public class UInt12Array
{
    // TODO: Constructors, etc. 

    private byte[] storage;

    public ushort this[int index] 
    {
        get
        {
            // TODO: throw exceptions if the index is off the array.
            int i = index * 2 / 3;
            if(index % 2 == 0)
                return (ushort)(((ushort)storage[i * 3 + 1]) << 8 & 0x0F00 | storage[i * 3 + 0]);
            else
                return (ushort)(((ushort)storage[i * 3 + 1]) << 4 | storage[i * 3 + 1] >> 4;)
        }
        set
        {
            // TODO: throw exceptions if the index is off the array.
            int i = index * 2 / 3;
            if(index % 2 == 0)
                storage[i * 3 + 0] = (byte)value;
                storage[i * 3 + 1] = (byte)(value >> 8 | storage[i * 3 + 1] & 0xF0);
            else
                storage[i * 3 + 1] = (byte)(storage[i * 3 + 1] & 0x0F | value << 4);
                storage[i * 3 + 2] = (byte)(value >> 4);

        }
    }
}


Why not store the 12-bit words in a byte array and provide a getter and a setter method that read and write the ushort's byte to the correct index in the array?


Trying to solve this with LINQ was fun!

Warning: For entertainment purposes only - do not use the below performance abominations in real code!

First try - group pairs of uints, create three bytes out of each pair, flatten list:

byte[] packedNumbers = (from i in Enumerable.Range(0, unpackedNumbers.Length)
                        group unpackedNumbers[i] by i - (i % 2) into pairs
                        let n1 = pairs.First()
                        let n2 = pairs.Skip(1).First()
                        let b1 = (byte)(n1 >> 4)
                        let b2 = (byte)(((n1 & 0xF) << 4) | (n2 & 0xF00) >> 8)
                        let b3 = (byte)(n2 & 0xFFFF)
                        select new[] { b1, b2, b3 })
                        .SelectMany(b => b).ToArray();

Or slightly more compact, but less readable:

byte[] packedNumbers = unpackedNumbers
  .Select((Value, Index) => new { Value, Index })
  .GroupBy(number => number.Index - (number.Index % 2))
  .SelectMany(pair => new byte[] {
    (byte)(pair.First().Value >> 4),
    (byte)(((pair.First().Value & 0xF) << 4) | (pair.Skip(1).First().Value & 0xF00) >> 8),
    (byte)(pair.Skip(1).First().Value & 0xFFFF) }).ToArray();

Strings anyone?

char[] hexChars = unpackedNumbers.SelectMany(n => n.ToString("X4").Substring(1, 3)).ToArray();
byte[] packedNumbers = (from i in Enumerable.Range(0, hexChars.Length / 2)
                        select byte.Parse(hexChars[i * 2].ToString() + hexChars[i * 2 + 1], NumberStyles.HexNumber))
                       .ToArray();


According to the comments given, I suppose, the current answers is preferable.

But about this should do it also:

public byte[] ushort2byteArr(ushort[] arr) {
  System.IO.MemoryStream ms = new System.IO.MemoryStream(); 
  System.IO.BinaryWriter bw = new System.IO.BinaryWriter(ms);
  for (int i = 0; i < arr.Length-1;) { // check upper limit! 
      // following is wrong! must extend this to pack 8 12 bit words into 3 uint32! 
      UInt32 tmp = arr[i++] | (arr[i++] << 12) ... ; 
      bw.Write(tmp);
  }
  return ms.ToArray(); 
}

its not tested. take it as pseudocode to get the clue. especially the word -> uint32 conversion. May need some padding at the end?

@edit: made a function out of it for better clearance

0

精彩评论

暂无评论...
验证码 换一张
取 消