0

我想创建一个 4 位(半字节)的自定义数据类型。

一种选择是 -

byte source = 0xAD;
var hiNybble = (source & 0xF0) >> 4; //Left hand nybble = A
var loNyblle = (source & 0x0F);      //Right hand nybble = D

但是,我想将每 4 位存储在一个数组中。

例如,一个 2 字节的数组,

00001111 01010000

将作为 4 个半字节存储在自定义数据类型数组中 -

0000
1111
0101
0000

本质上我想对 4 位类型进行操作。

有什么办法可以将字节数组转换为半字节数组?

欣赏一个例子。

谢谢。

4

3 回答 3

1

您可以通过读取然后转换来封装返回 4 位样本的流(从没有编译器的手机编写来测试。预期拼写错误和一个错误):

public static int ReadNibbles(this Stream s, byte[] data, int offset, int count)
{
    if (s == null)
    {
        throw new ArgumentNullException(nameof(s));
    }
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }
    if (data.Length < offset + length)
    {
        throw new ArgumentOutOfRangeException(nameof(length));
    }

    var readBytes = s.Read(data, offset, length / 2);
    for (int n = readBytes * 2 - 1, k = readBytes - 1; k >= 0; k--)
    {
        data[offset + n--] = data[offset + k] & 0xf;
        data[offset + n--] = data[offset + k] >> 4;
    }
    return readBytes * 2;
}

对 12 位整数执行相同操作(假设 MSB 半字节排序):

public static int Read(this Stream stream, ushort[] data, int offset, int length)
{
    if (stream == null)
    {
        throw new ArgumentNullException(nameof(stream));
    }
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }
    if (data.Length < offset + length)
    {
        throw new ArgumentOutOfRangeException(nameof(length));
    }
    if (length < 2)
    {
        throw new ArgumentOutOfRangeException(nameof(length), "Cannot read fewer than two samples at a time");
    }
        
    // we always need a multiple of two
    length -= length % 2;

    // 3 bytes     length samples
    // --------- * -------------- = N bytes
    // 2 samples         1
    int rawCount = (length / 2) * 3;

    // This will place GC load.  Something like a buffer pool or keeping
    // the allocation as a field on the reader would be a good idea.
    var rawData = new byte[rawCount];
    int readBytes = 0;
    // if the underlying stream returns an even number of bytes, we will need to try again
    while (readBytes < data.Length)
    {
        int c = stream.Read(rawData, readBytes, rawCount - readBytes);
        if (c <= 0)
        {
            // End of stream
            break;
        }
        readBytes += c;
    }

    // unpack
    int k = 0;
    for (int i = 0; i < readBytes; i += 3)
    {
        // EOF in second byte is undefined
        if (i + 1 >= readBytes)
        {
            throw new InvalidOperationException("Unexpected EOF");
        }

        data[(k++) + offset] = (ushort)((rawData[i + 0] << 4) | (rawData[i + 1] >> 4));

        // EOF in third byte == only one sample
        if (i + 2 < readBytes)
        {
            data[(k++) + offset] = (ushort)(((rawData[i + 1] & 0xf) << 8) | rawData[i + 2]);
        }
    }
    return k;
}
于 2021-01-17T04:33:31.643 回答
0

最好的方法是查看现有整数数据类型之一的来源。例如Int16.

如果您查看该类型,您会发现它实现了一些接口:

[Serializable]
public struct Int16 : IComparable, IFormattable, IConvertible, IComparable<short>, IEquatable<short> { /* ... */ }

该类型的实现不是很复杂。它有一个MaxValuea MinValue,几个CompareTo重载,几个Equals重载,System.Object覆盖(GetHashCode,,GetTypeToString加上一些重载)),一些ParseToParse重载以及一系列IConvertible实现。

在其他地方,您可以找到算术、比较和转换运算符之类的东西。

但是

System.Int16不能拥有的是:

internal short m_value;

这是保存该值的本机类型(16 位整数)成员。没有 4 位本机类型。您将能够做的最好的事情是在您的实现中拥有一个本机字节来保存该值。您可以编写将其限制为低 4 位的访问器,但除此之外您无能为力。如果有人创建了一个Nibble数组,它将被实现为这些值的数组。据我所知,没有办法将您的实现注入该数组。类似地,如果有人创建了一些其他集合(例如List<Nibble>),那么该集合将是您类型的实例,每个实例将占用 8 位。

然而

您可以创建专门的集合类、NibbleArrayNibbleList等。C# 的语法允许您为集合提供自己的集合初始化实现、您自己的索引方法等。

所以,如果有人这样做:

var nyblArray = new NibbleArray(32);
nyblArray[4] = 0xd;

然后,您的代码可以在幕后创建一个 16 元素字节数组,将第三个字节的低半字节设置为 0xd。

同样,您可以实现代码以允许:

var newArray = new NibbleArray { 0x1, 0x3, 0x5, 0x7, 0x9, 0xa};

或者

var nyblList = new NibbleList { 0x0, 0x2, 0xe};

一个普通的数组会浪费空间,但是你的专门的集合类会做你所说的(以一些位扭曲为代价)。

于 2021-01-19T03:25:44.660 回答
-1

最接近您想要的是使用索引器:

// Indexer declaration
public int this[int index]
{
    // get and set accessors
}

在索引器的主体中,您可以将索引转换为包含 4 位的实际字节。

您可以做的下一件事是运算符重载。您可以重新定义 +、-、*...

于 2021-01-17T04:34:56.963 回答