문제

나는 범위 0-63 내에 값의 배열을 모두 가지고 있으며, 값은 6 비트 만 필요하기 때문에 4 바이트를 3으로 포장 할 수 있다고 결정했습니다. 추가 2BIT를 사용하여 다음 값의 첫 2 비트를 저장할 수 있습니다. 곧.

내가 사용하기 전에이 일을 한 적이 없다 switch 진술과 a nextbit 포장을 수행하고 시작 비트를 추적하는 가변 (상태 머신). 그러나 나는 더 나은 방법이 있어야한다고 확신합니다.

제안/단서 제발 제발, 그러나 내 재미를 망치지 마십시오 ;-)

큰/작은 엔디언에 관한 휴대 성 문제가 있습니까?

BTW :이 코드가 다시 포장을 풀고 입력과 비교 하여이 코드가 작동하고 있음을 확인했습니다. 그리고 그것은 숙제가 아니라 내가 스스로 정한 운동 일뿐입니다.

/* build with gcc -std=c99 -Wconversion */
#define ASZ 400
typedef unsigned char uc_;
uc_ data[ASZ];
int i;
for (i = 0; i < ASZ; ++i) {
    data[i] = (uc_)(i % 0x40);
}
size_t dl = sizeof(data);
printf("sizeof(data):%z\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:%z\n",pl);

for (i = 0; i < dl; ++i)
    printf("%02d  ", data[i]);
printf("\n");

uc_ * packeddata = calloc(pl, sizeof(uc_));
uc_ * byte = packeddata;
uc_ nextbit = 1;
for (int i = 0; i < dl; ++i) {
    uc_ m = (uc_)(data[i] & 0x3f);
    switch(nextbit) {
    case 1:
        /* all 6 bits of m into first 6 bits of byte: */
        *byte = m;
        nextbit = 7;
        break;
    case 3:
        /* all 6 bits of m into last 6 bits of byte: */
        *byte++ = (uc_)(*byte | (m << 2));
        nextbit = 1;
        break;
    case 5:
        /* 1st 4 bits of m into last 4 bits of byte: */
        *byte++ = (uc_)(*byte | ((m & 0x0f) << 4));
        /* 5th and 6th bits of m into 1st and 2nd bits of byte: */
        *byte = (uc_)(*byte | ((m & 0x30) >> 4));
        nextbit = 3;
        break;
    case 7:
        /* 1st 2 bits of m into last 2 bits of byte: */
        *byte++ = (uc_)(*byte | ((m & 0x03) << 6));
        /* next (last) 4 bits of m into 1st 4 bits of byte: */
        *byte = (uc_)((m & 0x3c) >> 2);
        nextbit = 5;
        break;
    }
}
도움이 되었습니까?

해결책

그래서 이것은 좀 비슷합니다 코드 골프, 오른쪽?


#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;
}

다른 팁

IETF를 확인하십시오 RFC 4648 '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);

부동 소수점 물건을 사용하지 마십시오. 정수 만 사용하십시오. C99 라이브러리가 있다고 가정하면 '%z'를 사용하여 'size_t'값을 인쇄합니다.

size_t pl = ((dl + 3) / 4) * 3;

부분 단위가 남았을 때까지 3 바이트 입력 장치를 처리 한 다음 나머지 1 또는 2 바이트를 특별한 경우로 처리하여 루프를 단순화 할 수 있다고 생각합니다. 표준 참조에 따르면 끝에 패드에 하나 또는 두 개의 '='표지판을 사용한다고 말합니다.

Base64 인코더와 디코드가 있는데 그 중 일부를 수행합니다. Base64 코드에는 포장 코드로 3 바이트의 데이터가있는 4 바이트의 데이터가있는 Base64의 'Decode'부분을 설명합니다. Base64 인코더는 필요한 풀기에 해당합니다.

베이스 -64 디코더

참고 : Base_64_INV는 256 값의 배열이며, 가능한 각 입력 바이트 값당 하나는 하나입니다. 각 인코딩 된 바이트에 대한 올바른 디코딩 값을 정의합니다. Base64 인코딩에서 이것은 희소 배열 -3/4 0입니다. 마찬가지로, 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);
}

베이스 -64 인코더

/* 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));
}

Base64 인코딩에 변형 알파벳을 사용하는 제품을 처리하고 데이터를 패드하지 않아도되는 제품을 처리해야합니다. 따라서 'PAD'인수 ( 'NULL 패딩'또는 '표준의 경우 0이 될 수 있습니다. 패딩. 'base_64_map'배열에는 0..63 범위의 6 비트 값에 사용할 알파벳이 포함되어 있습니다.

또 다른 더 간단한 방법은 비트 필드를 사용하는 것입니다. c의 덜 알려진 모서리 중 하나 struct 구문은 큰 필드입니다. 다음 구조가 있다고 가정 해 봅시다.

struct packed_bytes {
    byte chunk1 : 6;
    byte chunk2 : 6;
    byte chunk3 : 6;
    byte chunk4 : 6;
};

이것은 선언합니다 chunk1, chunk2, chunk3, 그리고 chunk4 유형을 갖기 위해 byte 그러나 구조에서 6 비트 만 차지합니다. 결과는 그게됩니다 sizeof(struct packed_bytes) == 3. 이제 필요한 것은 배열을 가져 와서 다음과 같은 구조물에 버리는 작은 기능 만 있으면됩니다.

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];
    }
}

당신은 가서 이제 배열이 있습니다. struct packed_bytes 위의 구조물을 사용하여 쉽게 읽을 수 있습니다.

Statemachine을 사용하는 대신 현재 바이트에 이미 사용 된 비트 수에 대한 카운터를 사용하여 Shift-Offsets를 직접 도출 할 수 있으며 다음 바이트로 넘어가는지 여부를 사용할 수 있습니다. Endianess와 관련하여 : 단일 데이터 유형 만 사용하는 한 (즉, 다른 크기의 유형에 대한 포인터를 재 해석하지 않습니다 (예 : int* a =...;short* b=(short*) a;) 대부분의 경우 endianess에 문제가되지 않아야합니다.

DigitalRoss의 컴팩트 한 코드, Grizzly의 제안 및 내 코드의 요소를 취하면 마침내 내 자신의 답변을 작성했습니다. DigitalRoss는 유용한 작업 답변을 제공하지만 이해하지 못하는 저의 사용은 무언가를 배우는 것과 같은 만족을 제공하지 않았을 것입니다. 이런 이유로 나는 원래 코드를 바탕으로 답변을 선택했습니다.

또한 Jonathon Leffler가 포장 된 데이터 길이 계산을 위해 Float Point 산술을 사용하지 않도록 조언을 무시하기로 선택했습니다. 주어진 권장 방법 - 동일한 DigitalRoss도 사용하여 포장 된 데이터의 길이를 3 바이트만큼 증가시킵니다. 이것은 그다지 많지 않지만 부동 소수점 수학을 사용하면 피할 수 있습니다.

코드는 다음과 같습니다. 비판은 다음과 같습니다.

/* 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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top