Actualmente estoy trabajando en una herramienta de red que necesita decodificar/codificar un protocolo particular que empaqueta campos en matrices de bits densa en posiciones arbitrarias. Por ejemplo, una parte del protocolo utiliza 3 bytes para representar un número de diferentes campos:Extrayendo valores a través de límites de bytes con posiciones y longitudes de bits arbitrarios en C#
Bit Position(s) Length (In Bits) Type
0 1 bool
1-5 5 int
6-13 8 int
14-22 9 uint
23 1 bool
Como se puede ver, varios de los campos abarcan múltiples bytes. Muchos (la mayoría) también son más cortos que el tipo incorporado que podría usarse para representarlos, como el primer campo int que tiene solo 5 bits de longitud. En estos casos, los bits más significativos del tipo de destino (como un Int32 o Int16) deben rellenarse con 0 para compensar la diferencia.
Mi problema es que estoy teniendo dificultades para procesar este tipo de datos. Específicamente, me está resultando difícil descifrar cómo obtener de manera eficiente matrices de bits de longitud arbitraria, rellenarlas con los bits apropiados del buffer de origen, rellenarlas para que coincidan con el tipo de destino y convertir las matrices de bits rellenados al tipo de destino. En un mundo ideal, podría tomar el byte [3] en el ejemplo anterior y llamar a un método como GetInt32(byte[] bytes, int startBit, int length)
.
Lo más parecido en la naturaleza que he encontrado es una clase BitStream, pero parece querer que los valores individuales se alineen en los límites de bytes/palabras (y la convención de acceso a mitad de la transmisión/indexado de la clase lo hace un poco confuso).
Mi primer intento fue utilizar la clase BitArray, pero resultó algo difícil de manejar. Es bastante fácil rellenar todos los bits del búfer en un gran BitArray
, transferir solo los que desee de la fuente BitArray
a un nuevo BitArray
temporal, y luego convertir eso en el valor objetivo ... pero parece incorrecto, y muy pérdida de tiempo.
Ahora estoy considerando una clase como la siguiente que hace referencia (o crea) un buffer de origen/destino byte [] junto con un desplazamiento y proporciona métodos get y set para ciertos tipos de destino. La parte difícil es que obtener/establecer valores puede abarcar varios bytes.
class BitField
{
private readonly byte[] _bytes;
private readonly int _offset;
public BitField(byte[] bytes)
: this(bytes, 0)
{
}
public BitField(byte[] bytes, int offset)
{
_bytes = bytes;
_offset = offset;
}
public BitField(int size)
: this(new byte[size], 0)
{
}
public bool this[int bit]
{
get { return IsSet(bit); }
set { if (value) Set(bit); else Clear(bit); }
}
public bool IsSet(int bit)
{
return (_bytes[_offset + (bit/8)] & (1 << (bit % 8))) != 0;
}
public void Set(int bit)
{
_bytes[_offset + (bit/8)] |= unchecked((byte)(1 << (bit % 8)));
}
public void Clear(int bit)
{
_bytes[_offset + (bit/8)] &= unchecked((byte)~(1 << (bit % 8)));
}
//startIndex = the index of the bit at which to start fetching the value
//length = the number of bits to include - may be less than 32 in which case
//the most significant bits of the target type should be padded with 0
public int GetInt32(int startIndex, int length)
{
//NEED CODE HERE
}
//startIndex = the index of the bit at which to start storing the value
//length = the number of bits to use, if less than the number of bits required
//for the source type, precision may be lost
//value = the value to store
public void SetValue(int startIndex, int length, int value)
{
//NEED CODE HERE
}
//Other Get.../Set... methods go here
}
Busco a ningún tipo de orientación en esta área, tales como bibliotecas de terceros, los algoritmos para obtener/establecer los valores en las posiciones de bits arbitrarios que abarcan múltiples bytes, comentarios sobre mi enfoque, etc. incluyó la clase anterior para aclaración y no estoy necesariamente buscando el código para completarlo (¡aunque no discutiré si alguien quiere resolverlo!).
Algunas personas han mencionado endianness, y eso es ciertamente una preocupación. Como estoy tratando con datos de red, supongo que el buffer original está en big-endian (o en orden de red). Estaba planeando usar el excelente 'EndianBitConverter' de [MiscUtils] (http://www.yoda.arachsys.com/csharp/miscutil/) para convertir todo al orden local dentro de los métodos de Obtener ... – daveaglick
Todavía estoy trabajando en este problema. Ninguna de las respuestas a continuación era correcta para esta situación (en particular, los aspectos que implican posiciones arbitrarias y límites de byte que abarcan). He decidido implementar completamente la clase BitField presentada en la pregunta y he tenido algo de suerte. Debido a que puede tener muchos usos, especialmente para el procesamiento de red donde los campos de bits suelen estar empaquetados densamente, publicaré la clase completada en los próximos días una vez que se haya realizado como respuesta adicional. Seguiré votando otras respuestas útiles que aborden la pregunta. – daveaglick