如何在内存中按顺序存储可变长度代码?

时间:2012-08-17 23:32:49

标签: c arrays embedded

假设我有一个二维数组,其中每个条目包含一个长度和一个值:

int array[4][2] =  { /* {length, value}, */
                   {5, 3},
                   {6, 7},
                   {1, 0},
                   {8, 15},
                   };

我想将它们按顺序存储到带有前导零的内存中,以使每个字段的长度合适。上面的例子是:

    00011 000111 0 00001111

第一个块长5位,存储小数3.第二个块长6位,存储小数7。第三个块是一位长并存储十进制0,最后一个块是八位长并存储十进制15。

我可以通过一些按位操作来实现,但我想我会问是否有更简单的方法。

我使用C语言编写Tensilica 32位RISC处理器。

目的是编写一系列指数哥伦布码。

编辑:解决方案:

int main(int argc, char *argv[])
{
    unsigned int i = 0, j = 0;
    unsigned char bit = 0;
    unsigned int bit_num = 0;
    unsigned int field_length_bits = 0;
    unsigned int field_length_bytes = 0;
    unsigned int field_array_length = 0;
    unsigned int field_list[NUM_FIELDS][2] = {
                                            /*{Length, Value},*/ 
                                            {4,  3},
                                            {5,  5},
                                            {6,  9},
                                            {7,  11},
                                            {8,  13},
                                            {9, 15},
                                            {10, 17},
                                         };

    unsigned char *seq_array;

    // Find total length of field list in bits
    for (i = 0; i < NUM_FIELDS; i++) 
        field_length_bits += field_list[i][LENGTH];

    // Number of bytes needed to store FIELD parameters
    for (i = 0; i < (field_length_bits + i) % 8 != 0; i++) ;

    field_length_bytes = (field_length_bits + i) / 8;

    // Size of array we need to allocate (multiple of 4 bytes)
    for (i = 0; (field_length_bytes + i) % 4 != 0; i++) ;

    field_array_length = (field_length_bytes + i);

    // Allocate memory
    seq_array = (unsigned char *) calloc(field_array_length, sizeof(unsigned char));

    // Traverse source and set destination
    for(i = 0; i < NUM_FIELDS; i++)
    {
        for(j = 0; j < field_list[i][LENGTH]; j++)
        {
            bit = 0x01 & (field_list[i][VALUE] >> (field_list[i][LENGTH] - j - 1));
            if (bit)
                setBit(seq_array, field_array_length, bit_num, 1);
            else
                setBit(seq_array, field_array_length, bit_num, 0);
            bit_num++;

        }
    }

    return 0;
}



void setBit(unsigned char *array, unsigned int array_len, unsigned int bit_num, unsigned int bit_value)
{
    unsigned int byte_location = 0;
    unsigned int bit_location = 0;

    byte_location = bit_num / 8;
    if(byte_location > array_len - 1)
    {
        printf("setBit(): Unauthorized memory access");
        return;
    }
    bit_location = bit_num % 8;

    if(bit_value)
        array[byte_location] |= (1 << (7-bit_location));
    else
        array[byte_location] &= ~(1 << (7-bit_location)); 

    return;
}

3 个答案:

答案 0 :(得分:3)

您可以使用比特流库:

强烈推荐的比特流库:

http://cpansearch.perl.org/src/KURIHARA/Imager-QRCode-0.033/src/bitstream.c

http://cpansearch.perl.org/src/KURIHARA/Imager-QRCode-0.033/src/bitstream.h

因为这个比特流库似乎是非常独立的,并且似乎不需要外部包含。


http://www.codeproject.com/Articles/32783/CBitStream-A-simple-C-class-for-reading-and-writin - C库,但使用Windows WORD,DWORD类型(你仍然可以使用typedef来使用这个库)

http://code.google.com/p/youtube-mobile-ffmpeg/source/browse/trunk/libavcodec/bitstream.c?r=8 - 包含许多其他包含使用比特流库的文件


如果你只想要指数golomb代码,那么就有开源C实现:

http://www.koders.com/c/fid8A317DF502A7D61CC96EC4DA07021850B6AD97ED.aspx?s=gcd


或者您可以使用位操作技术。

例如:

unsigned int array[4][2] = ???
unsigned int mem[100] = {};
int index=0,bit=0;
for (int i=0;i<4;i++) {
  int shift = (32 - array[i][0] - bit);
  if (shift>0) mem[index] &= array[i][1] << shift;
  else {
    mem[index] &= array[i][1] >> -shift;
    mem[index+1] &= array[i][1] << (32+shift);
  }

  bit += array[i][1];

  if (bit>=32) {
    bit-=32;
    index++;
  }
}

声明:

代码仅在您的计算机字节顺序为小端的情况下有效,并且结果实际上在每个4字节边界内是小端,而跨越4字节边界的是大端。如果将mem从int类型转换为char,并将常量32替换为8,则将得到位数组的大端表示。

它还假设长度小于32.显然,您实际想要的代码将取决于有效输入的边界,以及您在字节排序方面的要求。

答案 1 :(得分:2)

你的意思是位字段吗?

struct myBF
{
    unsigned int v1 : 5;
    unsigned int v2 : 5;
    unsigned int v3 : 1;
    unsigned int v4 : 8;
};

struct myBF b = { 3, 7, 0, 15 };

我可能完全误解了你的要求。如果是这样,请评论。


更新:假设您要动态动态。让我们创建一个接受一对数组的函数,如示例和输出缓冲区:

/* Fill dst with bits.
 * Returns one plus the number of bytes used or 0 on error.
 */
size_t bitstream(int (*arr)[2], size_t arrlen,
                          unsigned char * dst, size_t dstlen)
{
    size_t total_bits = 0, bits_so_far = 0;

    /* Check if there's enough space */
    for (size_t i = 0; i != arrlen; ++i) { total_bits += arr[i][0]; }
    if (dst == NULL || total_bits > CHAR_BIT * dstlen)  { return 0; }

    /* Set the output range to all zero */
    memset(dst, 0, dstlen);

    /* Populate the output range */
    for (size_t i = 0; i != arrlen; ++i)
    {
        for (size_t bits_to_spend = arr[i][0], value = arr[i][1];
             bits_to_spend != 0; /* no increment */ )
        {
            size_t const bit_offset = bits_so_far % CHAR_BIT;
            size_t const byte_index = bits_so_far / CHAR_BIT;
            size_t const cur_byte_capacity = CHAR_BIT - bit_offset;

            /* Debug: Watch it work! */
            printf("Need to store %zu, %zu bits to spend, capacity %zu.\n",
                   value, bits_to_spend, cur_byte_capacity);

            dst[byte_index] |= (value << bit_offset);

            if (cur_byte_capacity < bits_to_spend)
            {
                value        >>= cur_byte_capacity;
                bits_so_far   += cur_byte_capacity;
                bits_to_spend -= cur_byte_capacity;
            }
            else
            {
                bits_so_far += bits_to_spend;
                bits_to_spend = 0;
            }
        }
    }

    return (bits_so_far + CHAR_BIT - 1) / CHAR_BIT;
}

注意:

  • 如果数字arr[i][1]不适合arr[i][0]位,则仅存储模2 arr[i][0] 的残差。

    < / LI>
  • 为了完全正确,数组类型也应该是无符号的,否则初始化size_t value = arr[i][1]可能是未定义的行为。

  • 您可以修改错误处理行为。例如,您可以放弃事务性并将长度检查移动到主循环中。此外,您可以返回必需字节的数量,而不是返回0,以便用户可以确定目标数组需要多大(如snptrintf所做的那样)。

用法:

unsigned char dst[N];
size_t n = bitstream(array, sizeof array / sizeof *array, dst, sizeof dst);
for (size_t i = 0; i != n; ++i) { printf("0x%02X ", dst[n - i - 1]); }

对于您的示例,这将生成0x00 0xF0 0xE3,即:

  0x00     0xF0     0xE3
00000000 11110000 11100011

0000 00001111 0 000111 00011
padd    15    0    7     3

答案 2 :(得分:0)

在标准C中,除了你提到的“按位操作”以外,没有办法以任何方式访问小于char的任何东西。我担心你运气不好,除非你遇到一个可以帮助你的图书馆。