Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 将4个字节打包成3个字节的更好方法是什么?_C_Arrays_Bit Manipulation_Bit Packing - Fatal编程技术网

C 将4个字节打包成3个字节的更好方法是什么?

C 将4个字节打包成3个字节的更好方法是什么?,c,arrays,bit-manipulation,bit-packing,C,Arrays,Bit Manipulation,Bit Packing,我有一个值数组,所有值都在0-63范围内,我决定可以将每4个字节打包成3个,因为这些值只需要6位,我可以使用额外的2位来存储下一个值的前2位,依此类推 在此之前,我从未这样做过,我使用switch语句和nextbit变量(类似于状态机的设备)来打包并跟踪起始位。但我相信,一定有更好的办法 请提供建议/线索,但不要破坏我的乐趣;-) 关于big/little-endian是否存在可移植性问题 顺便说一句:通过再次解包并与输入进行比较,我已经验证了该代码是否正常工作。不,这不是家庭作业,只是我自己做

我有一个值数组,所有值都在0-63范围内,我决定可以将每4个字节打包成3个,因为这些值只需要6位,我可以使用额外的2位来存储下一个值的前2位,依此类推

在此之前,我从未这样做过,我使用
switch
语句和
nextbit
变量(类似于状态机的设备)来打包并跟踪起始位。但我相信,一定有更好的办法

请提供建议/线索,但不要破坏我的乐趣;-)

关于big/little-endian是否存在可移植性问题

顺便说一句:通过再次解包并与输入进行比较,我已经验证了该代码是否正常工作。不,这不是家庭作业,只是我自己做的练习

/*使用gcc构建-std=c99-Wconversion*/
#定义ASZ 400
typedef无符号字符uc;
uc_uu数据[ASZ];
int i;
对于(i=0;i(浮动)(整数)fpl)?fpl+1:fpl;
printf(“压缩数据的长度:%z\n”,pl);
对于(i=0;i2);
nextbit=5;
打破
}
}
查看IETF中的“Base16、Base32和Base64数据编码”

部分代码评论:

size_t dl = sizeof(data);
printf("sizeof(data):%d\n",dl);
float fpl = ((float)dl / 4.0f) * 3.0f;
size_t pl = (size_t)(fpl > (float)((int)fpl) ? fpl + 1 : fpl);
printf("length of packed data:%d\n",pl);
不要使用浮点数-只使用整数。使用'%z'打印'size\u t'值-假设您有一个C99库

size_t pl = ((dl + 3) / 4) * 3;
我认为你的循环可以通过处理3字节的输入单元来简化,直到你有一个剩余的部分单元,然后作为特殊情况处理剩余的1或2字节。我注意到引用的标准说你在最后使用一个或两个“=”符号来填充

我有一个Base64编码器和解码,它可以做一些工作。您正在描述Base64的“解码”部分——其中Base64代码有4个字节的数据,应该存储在3个字节中,作为打包代码。Base64编码器对应于您需要的解包器

Base-64解码器 注意:base_64_inv是一个256个值的数组,每个可能的输入字节值一个;它为每个编码字节定义正确的解码值。在Base64编码中,这是一个稀疏数组-3/4零。类似地,base_64_map是值0..63和相应存储值之间的映射

enum { DC_PAD = -1, DC_ERR = -2 };

static int decode_b64(int c)
{
    int b64 = base_64_inv[c];

    if (c == base64_pad)
        b64 = DC_PAD;
    else if (b64 == 0 && c != base_64_map[0])
        b64 = DC_ERR;
    return(b64);
}

/* Decode 4 bytes into 3 */
static int decode_quad(const char *b64_data, char *bin_data)
{
    int b0 = decode_b64(b64_data[0]);
    int b1 = decode_b64(b64_data[1]);
    int b2 = decode_b64(b64_data[2]);
    int b3 = decode_b64(b64_data[3]);
    int bytes;

    if (b0 < 0 || b1 < 0 || b2 == DC_ERR || b3 == DC_ERR || (b2 == DC_PAD && b3 != DC_PAD))
        return(B64_ERR_INVALID_ENCODED_DATA);
    if (b2 == DC_PAD && (b1 & 0x0F) != 0)
        /* 3rd byte is '='; 2nd byte must end with 4 zero bits */
        return(B64_ERR_INVALID_TRAILING_BYTE);
    if (b2 >= 0 && b3 == DC_PAD && (b2 & 0x03) != 0)
        /* 4th byte is '='; 3rd byte is not '=' and must end with 2 zero bits */
        return(B64_ERR_INVALID_TRAILING_BYTE);
    bin_data[0] = (b0 << 2) | (b1 >> 4);
    bytes = 1;
    if (b2 >= 0)
    {
        bin_data[1] = ((b1 & 0x0F) << 4) | (b2 >> 2);
        bytes = 2;
    }
    if (b3 >= 0)
    {
        bin_data[2] = ((b2 & 0x03) << 6) | (b3);
        bytes = 3;
    }
    return(bytes);
}

/* Decode input Base-64 string to original data.  Output length returned, or negative error */
int base64_decode(const char *data, size_t datalen, char *buffer, size_t buflen)
{
    size_t outlen = 0;
    if (datalen % 4 != 0)
        return(B64_ERR_INVALID_ENCODED_LENGTH);
    if (BASE64_DECLENGTH(datalen) > buflen)
        return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL);
    while (datalen >= 4)
    {
        int nbytes = decode_quad(data, buffer + outlen);
        if (nbytes < 0)
            return(nbytes);
        outlen += nbytes;
        data += 4;
        datalen -= 4;
    }
    assert(datalen == 0);   /* By virtue of the %4 check earlier */
    return(outlen);
}
enum{DC_PAD=-1,DC_ERR=-2};
静态整数解码_b64(整数c)
{
int b64=基本库存量[c];
如果(c==base64_焊盘)
b64=DC_焊盘;
else if(b64==0&&c!=base\u 64\u map[0])
b64=直流电故障;
返回(b64);
}
/*将4字节解码为3字节*/
静态整数解码四元组(常量字符*b64数据,字符*二进制数据)
{
int b0=解码b64(b64数据[0]);
int b1=解码b64(b64数据[1]);
int b2=解码b64(b64数据[2]);
int b3=解码b64(b64数据[3]);
整数字节;
如果(b0<0 | | b1<0 | | b2==DC|ERR | | b3==DC|ERR | |(b2==DC|PAD&b3!=DC|PAD))
返回(B64错误无效编码数据);
如果(b2==DC_焊盘和(b1&0x0F)!=0)
/*第三个字节为“=”;第二个字节必须以4个零位结尾*/
返回(B64错误无效尾随字节);
如果(b2>=0&&b3==DC\U焊盘&&(b2&0x03)!=0)
/*第四个字节是“=”;第三个字节不是“=”,必须以2个零位结尾*/
返回(B64错误无效尾随字节);
bin_数据[0]=(b0>4);
字节=1;
如果(b2>=0)
{
bin_数据[1]=((b1和0x0F)>2);
字节=2;
}
如果(b3>=0)
{
bin_数据[2]=((b2和0x03)buflen)
返回(B64错误输出缓冲区太小);
而(数据长度>=4)
{
int nbytes=解码四元组(数据、缓冲区+输出);
如果(n字节<0)
返回(单位:N字节);
outlen+=n字节;
数据+=4;
datalen-=4;
}
通过前面的%4检查断言(datalen==0);/**/
返回(outlen);
}
Base-64编码器
/*将3个字节的数据编码为4个字节*/
静态void encode_三元组(常量字符*三元组,字符*四元组)
{
四元组[0]=基本64_映射[(三元组[0]>>2)&0x3F];
四元组[1]=基组64_映射[(三元组[0]和0x03)>4)和0x0F];
四元组[2]=基组64_映射[(三元组[1]和0x0F)>6)和0x03];
四元组[3]=基本组64映射[triplet[2]&0x3F];
}
/*将2字节的数据编码为4字节*/
静态无效编码双精度(常量字符*双精度、字符*四精度、字符填充)
{
四元组[0]=基本64_映射[(双元组[0]>>2)&0x3F];
四元组[1]=基本64_映射[(双元组[0]和0x03)>4)和0x0F];
四元组[2]=基64映射[(双元组[1]和0x0F)>2)和0x3F];
四元组[1]=基本组64映射[(单态[0]&0x03)buflen)
返回(B64错误输出缓冲区太小);
而(数据长度>=3)
{
编码_三元组(bin_数据、b64_数据);
bin_数据+=3;
b64_数据+=4;
datalen-=3;
}
b64_数据[0]='\0';
如果(数据长度==2)
编码双绞线(bin双绞线数据、b64双绞线数据、pad);
else if(datalen==1)
编码单重态(二进制数据、b64二进制数据、pad);
b64_数据[4]='\0';
返回((b64_数据-缓冲)+strlen(b64_数据));
}
我不得不处理一个使用变体字母表进行Base64编码的产品,并设法不填充数据,这使我的生活变得复杂化,因此“pad”参数(对于“null padding”可以为零,对于标准padding可以为“=”。“base_64_map”数组包含用于6位val的字母表
enum { DC_PAD = -1, DC_ERR = -2 };

static int decode_b64(int c)
{
    int b64 = base_64_inv[c];

    if (c == base64_pad)
        b64 = DC_PAD;
    else if (b64 == 0 && c != base_64_map[0])
        b64 = DC_ERR;
    return(b64);
}

/* Decode 4 bytes into 3 */
static int decode_quad(const char *b64_data, char *bin_data)
{
    int b0 = decode_b64(b64_data[0]);
    int b1 = decode_b64(b64_data[1]);
    int b2 = decode_b64(b64_data[2]);
    int b3 = decode_b64(b64_data[3]);
    int bytes;

    if (b0 < 0 || b1 < 0 || b2 == DC_ERR || b3 == DC_ERR || (b2 == DC_PAD && b3 != DC_PAD))
        return(B64_ERR_INVALID_ENCODED_DATA);
    if (b2 == DC_PAD && (b1 & 0x0F) != 0)
        /* 3rd byte is '='; 2nd byte must end with 4 zero bits */
        return(B64_ERR_INVALID_TRAILING_BYTE);
    if (b2 >= 0 && b3 == DC_PAD && (b2 & 0x03) != 0)
        /* 4th byte is '='; 3rd byte is not '=' and must end with 2 zero bits */
        return(B64_ERR_INVALID_TRAILING_BYTE);
    bin_data[0] = (b0 << 2) | (b1 >> 4);
    bytes = 1;
    if (b2 >= 0)
    {
        bin_data[1] = ((b1 & 0x0F) << 4) | (b2 >> 2);
        bytes = 2;
    }
    if (b3 >= 0)
    {
        bin_data[2] = ((b2 & 0x03) << 6) | (b3);
        bytes = 3;
    }
    return(bytes);
}

/* Decode input Base-64 string to original data.  Output length returned, or negative error */
int base64_decode(const char *data, size_t datalen, char *buffer, size_t buflen)
{
    size_t outlen = 0;
    if (datalen % 4 != 0)
        return(B64_ERR_INVALID_ENCODED_LENGTH);
    if (BASE64_DECLENGTH(datalen) > buflen)
        return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL);
    while (datalen >= 4)
    {
        int nbytes = decode_quad(data, buffer + outlen);
        if (nbytes < 0)
            return(nbytes);
        outlen += nbytes;
        data += 4;
        datalen -= 4;
    }
    assert(datalen == 0);   /* By virtue of the %4 check earlier */
    return(outlen);
}
/* Encode 3 bytes of data into 4 */
static void encode_triplet(const char *triplet, char *quad)
{
    quad[0] = base_64_map[(triplet[0] >> 2) & 0x3F];
    quad[1] = base_64_map[((triplet[0] & 0x03) << 4) | ((triplet[1] >> 4) & 0x0F)];
    quad[2] = base_64_map[((triplet[1] & 0x0F) << 2) | ((triplet[2] >> 6) & 0x03)];
    quad[3] = base_64_map[triplet[2] & 0x3F];
}

/* Encode 2 bytes of data into 4 */
static void encode_doublet(const char *doublet, char *quad, char pad)
{
    quad[0] = base_64_map[(doublet[0] >> 2) & 0x3F];
    quad[1] = base_64_map[((doublet[0] & 0x03) << 4) | ((doublet[1] >> 4) & 0x0F)];
    quad[2] = base_64_map[((doublet[1] & 0x0F) << 2)];
    quad[3] = pad;
}

/* Encode 1 byte of data into 4 */
static void encode_singlet(const char *singlet, char *quad, char pad)
{
    quad[0] = base_64_map[(singlet[0] >> 2) & 0x3F];
    quad[1] = base_64_map[((singlet[0] & 0x03) << 4)];
    quad[2] = pad;
    quad[3] = pad;
}

/* Encode input data as Base-64 string.  Output length returned, or negative error */
static int base64_encode_internal(const char *data, size_t datalen, char *buffer, size_t buflen, char pad)
{
    size_t outlen = BASE64_ENCLENGTH(datalen);
    const char *bin_data = (const void *)data;
    char *b64_data = (void *)buffer;

    if (outlen > buflen)
        return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL);
    while (datalen >= 3)
    {
        encode_triplet(bin_data, b64_data);
        bin_data += 3;
        b64_data += 4;
        datalen -= 3;
    }
    b64_data[0] = '\0';

    if (datalen == 2)
        encode_doublet(bin_data, b64_data, pad);
    else if (datalen == 1)
        encode_singlet(bin_data, b64_data, pad);
    b64_data[4] = '\0';
    return((b64_data - buffer) + strlen(b64_data));
}
struct packed_bytes {
    byte chunk1 : 6;
    byte chunk2 : 6;
    byte chunk3 : 6;
    byte chunk4 : 6;
};
void
dump_to_struct(byte *in, struct packed_bytes *out, int count)
{
    int i, j;
    for (i = 0; i < (count / 4); ++i) {
        out[i].chunk1 = in[i * 4];
        out[i].chunk2 = in[i * 4 + 1];
        out[i].chunk3 = in[i * 4 + 2];
        out[i].chunk4 = in[i * 4 + 3];
    }
    // Finish up
    switch(struct % 4) {
    case 3:
        out[count / 4].chunk3 = in[(count / 4) * 4 + 2];
    case 2:
        out[count / 4].chunk2 = in[(count / 4) * 4 + 1];
    case 1:
        out[count / 4].chunk1 = in[(count / 4) * 4];
    }
}
#include <stdlib.h>
#include <string.h>

static void pack2(unsigned char *r, unsigned char *n) {
  unsigned v = n[0] + (n[1] << 6) + (n[2] << 12) + (n[3] << 18);
  *r++ = v;
  *r++ = v >> 8;
  *r++ = v >> 16;
}

unsigned char *apack(const unsigned char *s, int len) {
  unsigned char *s_end = s + len,
                *r, *result = malloc(len/4*3+3),
                lastones[4] = { 0 };
  if (result == NULL)
    return NULL;
  for(r = result; s + 4 <= s_end; s += 4, r += 3)
    pack2(r, s);
  memcpy(lastones, s, s_end - s);
  pack2(r, lastones);
  return result;
}
/* built with gcc -std=c99 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned char *
pack(const unsigned char * data, size_t len, size_t * packedlen)
{
    float fpl = ((float)len / 4.0f) * 3.0f;
    *packedlen = (size_t)(fpl > (float)((int)fpl) ? fpl + 1 : fpl);
    unsigned char * packed = malloc(*packedlen);
    if (!packed)
        return 0;
    const unsigned char * in = data;
    const unsigned char * in_end = in + len;
    unsigned char * out;
    for (out = packed; in + 4 <= in_end; in += 4) {
        *out++ = in[0] | ((in[1] & 0x03) << 6);
        *out++ = ((in[1] & 0x3c) >> 2) | ((in[2] & 0x0f) << 4);
        *out++ = ((in[2] & 0x30) >> 4) | (in[3] << 2);
    }
    size_t lastlen = in_end - in;
    if (lastlen > 0) {
        *out = in[0];
        if (lastlen > 1) {
            *out++ |= ((in[1] & 0x03) << 6);
            *out = ((in[1] & 0x3c) >> 2);
            if (lastlen > 2) {
                *out++ |= ((in[2] & 0x0f) << 4);
                *out = ((in[2] & 0x30) >> 4);
                if (lastlen > 3)
                    *out |= (in[3] << 2);
            }
        }
    }
    return packed;
}

int main()
{
    size_t i;
    unsigned char data[] = {
        12, 15, 40, 18,
        26, 32, 50, 3,
        7,  19, 46, 10,
        25, 37, 2,  39,
        60, 59, 0,  17,
        9,  29, 13, 54,
        5,  6,  47, 32
    };
    size_t datalen = sizeof(data);
    printf("unpacked datalen: %td\nunpacked data\n", datalen);
    for (i = 0; i < datalen; ++i)
        printf("%02d  ", data[i]);
    printf("\n");
    size_t packedlen;
    unsigned char * packed = pack(data, sizeof(data), &packedlen);
    if (!packed) {
        fprintf(stderr, "Packing failed!\n");
        return EXIT_FAILURE;
    }
    printf("packedlen: %td\npacked data\n", packedlen);
    for (i = 0; i < packedlen; ++i)
        printf("0x%02x ", packed[i]);
    printf("\n");
    free(packed);
    return EXIT_SUCCESS;
}