Tính toán bảng CRC32 tại thời gian biên dịch [đóng]


16

Việc triển khai tham chiếu của CRC32 tính toán bảng tra cứu khi chạy:

/* Table of CRCs of all 8-bit messages. */
unsigned long crc_table[256];

/* Flag: has the table been computed? Initially false. */
int crc_table_computed = 0;

/* Make the table for a fast CRC. */
void make_crc_table(void)
{
    unsigned long c;

    int n, k;
    for (n = 0; n < 256; n++) {
        c = (unsigned long) n;
        for (k = 0; k < 8; k++) {
            if (c & 1) {
                c = 0xedb88320L ^ (c >> 1);
            } else {
                c = c >> 1;
            }
        }
        crc_table[n] = c;
    }
    crc_table_computed = 1;
}

Bạn có thể tính toán bảng trong thời gian biên dịch, do đó loại bỏ hàm và cờ trạng thái không?


2
Tiêu chí chiến thắng chính khách quan ở đây là gì?
John Dvorak

Ngoài ra, câu hỏi cụ thể về ngôn ngữ được tán thành ở đây. bạn nên xóa thẻ c ++.
tự hào

Câu trả lời:


12

Đây là một giải pháp C đơn giản:

crc32table.c

#if __COUNTER__ == 0

    /* Initial setup */
    #define STEP(c) ((c)>>1 ^ ((c)&1 ? 0xedb88320L : 0))
    #define CRC(n) STEP(STEP(STEP(STEP(STEP(STEP(STEP(STEP((unsigned long)(n)))))))))
    #define CRC4(n) CRC(n), CRC(n+1), CRC(n+2), CRC(n+3)

    /* Open up crc_table; subsequent iterations will fill its members. */
    const unsigned long crc_table[256] = {

    /* Include recursively for next iteration. */
    #include "crc32table.c"

#elif __COUNTER__ < 256 * 3 / 4

    /* Fill the next four CRC entries. */
    CRC4((__COUNTER__ - 3) / 3 * 4),

    /* Include recursively for next iteration. */
    #include "crc32table.c"

#else

    /* Close crc_table. */
    };

#endif

Nó dựa vào __COUNTER__macro không chuẩn , cũng như một ngữ nghĩa đánh giá, nơi __COUNTER__được đánh giá trước khi nó được chuyển qua làm đối số cho macro.

Lưu ý rằng, vì STEPđánh giá đối số của nó hai lần và CRCsử dụng tám yêu cầu lồng nhau của nó, có một vụ nổ tổ hợp nhỏ trong kích thước mã:

$ cpp crc32table.c | wc -c
4563276

Tôi đã thử nghiệm điều này trong GCC 4.6.0 và Clang 2.8 trên Linux 32 bit và cả hai đều tạo ra bảng chính xác.


Tuyệt vời, tôi chắc chắn đã không mong đợi điều này. Có +1.
R. Martinho Fernandes

9

Vòng lặp cốt lõi

for (k = 0; k < 8; k++) {
    if (c & 1) {
        c = 0xedb88320L ^ (c >> 1);
    } else {
        c = c >> 1;
    }
}

có thể được chuyển đổi thành một hàm meta:

template <unsigned c, int k = 8>
struct f : f<((c & 1) ? 0xedb88320 : 0) ^ (c >> 1), k - 1> {};

template <unsigned c>
struct f<c, 0>
{
    enum { value = c };
};

Sau đó, 256 lệnh gọi đến hàm meta này (đối với trình khởi tạo mảng) được tạo bởi bộ tiền xử lý:

#define A(x) B(x) B(x + 128)
#define B(x) C(x) C(x +  64)
#define C(x) D(x) D(x +  32)
#define D(x) E(x) E(x +  16)
#define E(x) F(x) F(x +   8)
#define F(x) G(x) G(x +   4)
#define G(x) H(x) H(x +   2)
#define H(x) I(x) I(x +   1)
#define I(x) f<x>::value ,

unsigned crc_table[] = { A(0) };

Nếu bạn đã cài đặt Boost, việc tạo trình khởi tạo mảng sẽ đơn giản hơn một chút:

#include <boost/preprocessor/repetition/enum.hpp>

#define F(Z, N, _) f<N>::value

unsigned crc_table[] = { BOOST_PP_ENUM(256, F, _) };

Cuối cùng, trình điều khiển kiểm tra sau chỉ đơn giản là in tất cả các thành phần mảng sang bàn điều khiển:

#include <cstdio>

int main()
{
    for (int i = 0; i < 256; ++i)
    {
        printf("%08x  ", crc_table[i]);
    }
}

6

Một giải pháp C ++ 0x

template<unsigned long C, int K = 0>
struct computek {
  static unsigned long const value = 
    computek<(C & 1) ? (0xedb88320L ^ (C >> 1)) : (C >> 1), K+1>::value;
};

template<unsigned long C>
struct computek<C, 8> {
  static unsigned long const value = C;
};

template<int N = 0, unsigned long ...D>
struct compute : compute<N+1, D..., computek<N>::value> 
{ };

template<unsigned long ...D>
struct compute<256, D...> {
  static unsigned long const crc_table[sizeof...(D)];
};

template<unsigned long...D>
unsigned long const compute<256, D...>::crc_table[sizeof...(D)] = { 
  D...
};

/* print it */
#include <iostream>

int main() {
  for(int i = 0; i < 256; i++)
    std::cout << compute<>::crc_table[i] << std::endl;
}

Hoạt động trên GCC (4.6.1) và Clang (thân 134121).


Về việc C >> 1, không chuyển các giá trị tiêu cực sang hành vi không xác định đúng không? ;)
dòng chảy

Ngoài ra, bạn có thể giải thích phần mà bạn xác định mảng? Tôi có một chút mất mát ở đó ...
fredoverflow

@Fred bạn nói đúng. Tôi cũng sẽ làm cho Cmột unsigned long. Mảng không đổi được định nghĩa sẽ được khởi tạo bằng cách mở rộng gói D.... Dlà một gói tham số mẫu không loại. Khi GCC hỗ trợ nó, người ta cũng có thể khai báo mảng trong lớp static unsigned long constexpr crc_table[] = { D... };, nhưng GCC chưa phân tích cú pháp khởi tạo trong lớp. Lợi ích sẽ là compute<>::crc_table[I]có thể được sử dụng trong các biểu thức không đổi sau này trong mã.
Julian Schaub - litb

5

C ++ 0x với constexpr. Hoạt động trên GCC4.6.1

constexpr unsigned long computek(unsigned long c, int k = 0) {
  return k < 8 ? computek((c & 1) ? (0xedb88320L ^ (c >> 1)) : (c >> 1), k+1) : c;
}

struct table {
  unsigned long data[256];
};

template<bool> struct sfinae { typedef table type; };
template<> struct sfinae<false> { };

template<typename ...T>
constexpr typename sfinae<sizeof...(T) == 256>::type compute(int n, T... t) { 
  return table {{ t... }}; 
}

template<typename ...T>
constexpr typename sfinae<sizeof...(T) <= 255>::type compute(int n, T... t) {
  return compute(n+1, t..., computek(n));
}

constexpr table crc_table = compute(0);

#include <iostream>

int main() {
  for(int i = 0; i < 256; i++)
    std::cout << crc_table.data[i] << std::endl;
}

Sau đó bạn có thể sử dụng crc_table.data[X]tại thời gian biên dịch vì crc_tableconstexpr.


4

Đây là siêu biểu đồ đầu tiên của tôi :

#include <cassert>
#include <cstddef>

template <std::size_t N, template <unsigned long> class T, unsigned long In>
struct times : public T<times<N-1,T,In>::value> {};

template <unsigned long In, template <unsigned long> class T>
struct times<1,T,In> : public T<In> {};

template <unsigned long C>
struct iter {
    enum { value = C & 1 ? 0xedb88320L ^ (C >> 1) : (C >> 1) };
};

template <std::size_t N>
struct compute : public times<8,iter,N> {};

unsigned long crc_table[] = {
    compute<0>::value,
    compute<1>::value,
    compute<2>::value,
    // .
    // .
    // .
    compute<254>::value,
    compute<255>::value,
};

/* Reference Table of CRCs of all 8-bit messages. */
unsigned long reference_table[256];

/* Flag: has the table been computed? Initially false. */
int reference_table_computed = 0;

/* Make the table for a fast CRC. */
void make_reference_table(void)
{
    unsigned long c;

    int n, k;
    for (n = 0; n < 256; n++) {
        c = (unsigned long) n;
        for (k = 0; k < 8; k++) {
            if (c & 1) {
                c = 0xedb88320L ^ (c >> 1);
            } else {
                c = c >> 1;
            }
        }
        reference_table[n] = c;
    }
    reference_table_computed = 1;
}

int main() {
    make_reference_table();
    for(int i = 0; i < 256; ++i) {
        assert(crc_table[i] == reference_table[i]);
    }
}

Tôi "mã hóa" các cuộc gọi đến mẫu thực hiện tính toán :)


1
Nếu bạn sẽ làm theo cách đó, tại sao bạn không mã hóa các giá trị thực tế vào chương trình? (Tất nhiên sau khi có được chúng bằng một phương pháp khác.) Tiết kiệm thời gian biên dịch.
Matthew Đọc

+1 cho bài kiểm tra và timesmẫu
fredoverflow

@Matthew: chỉ với C ++ 03, có rất ít sự lựa chọn. Bạn có thể sử dụng bộ tiền xử lý, giống như Fred đã làm, nhưng điều đó sẽ không rút ngắn thời gian biên dịch. Và rõ ràng, bộ tiền xử lý của anh ấy đã bóp nghẹt giải pháp của anh ấy :)
R. Martinho Fernandes

@Matthew Vấn đề là thực sự tính toán nó vào thời gian biên dịch , không để chúng bị mã hóa cứng. Câu trả lời của Fred tạo ra một mảng của mẫu này: unsigned crc_table[] = { f<0>::value , f<0 + 1>::value , f<0 + 2>::value , f<0 + 2 + 1>::value , f<0 + 4>::value , f<0 + 4 + 1>::value , f<0 + 4 + 2>::value , f<0 + 4 + 2 + 1>::value , f<0 + 8>::value ,sử dụng bộ tiền xử lý. Nó mất khoảng thời gian để biên dịch như của tôi. Nếu bạn muốn, bạn có thể đọc đoạn cuối đó là "Tôi đã bỏ vòng lặp bên ngoài". Không có lựa chọn nào khác trong C ++ 03
R. Martinho Fernandes

Ah, tôi đã không chú ý đầy đủ đến các yêu cầu của câu hỏi. +1, mặc dù tôi không chắc là tôi thích câu hỏi này nữa. Tôi thích mã của tôi thách thức thực tế: P
Matthew Đọc

3

D

import std.stdio, std.conv;

string makeCRC32Table(string name){

  string result = "immutable uint[256]"~name~" = [ ";

  for(uint n; n < 256; n++){
    uint c = n;
    for(int k; k < 8; k++)
      c = (c & 1) ? 0xedb88320L ^ (c >> 1) : c >>1;
    result ~= to!string(c) ~ ", ";
  }
  return result ~ "];";
}

void main(){

  /* fill table during compilation */
  mixin(makeCRC32Table("crc_table"));

  /* print the table */
  foreach(c; crc_table)
    writeln(c);
}

Nó thực sự khiến C ++ phải xấu hổ, phải không?


2
Trên thực tế, tôi thích các giải pháp không đánh máy. Điều này trông rất giống như thời gian biên dịch eval.
R. Martinho Fernandes

Không cần sử dụng chuỗi mixin cho việc này, đây là cách chúng tôi thực hiện trong thư viện chuẩn, genTablestrang web cuộc gọi của D để lưu trữ kết quả trong phân đoạn dữ liệu const.
Martin Nowak

3

C / C ++, 306 295 byte

#define C(c)((c)>>1^((c)&1?0xEDB88320L:0))
#define K(c)(C(C(C(C(C(C(C(C(c))))))))),
#define F(h,l)K((h)|(l+0))K((h)|(l+1))K((h)|(l+2))K((h)|(l+3))
#define R(h)F(h<<4,0)F(h<<4,4)F(h<<4,8)F(h<<4,12)
unsigned long crc_table[]={R(0)R(1)R(2)R(3)R(4)R(5)R(6)R(7)R(8)R(9)R(10)R(11)R(12)R(13)R(14)R(15)};

Làm việc ngược lại, chúng tôi kết thúc với một mảng dài không dấu có tên là crc_table. Chúng ta có thể bỏ qua kích thước của mảng vì các macro sẽ đảm bảo có chính xác 256 phần tử trong mảng. Chúng tôi khởi tạo mảng với 16 'hàng' dữ liệu bằng cách sử dụng 16 yêu cầu của macro R.

Mỗi lệnh gọi R mở rộng thành bốn đoạn (macro F) gồm bốn hằng số (macro K) cho tổng số 16 'cột' dữ liệu.

Macro K là vòng lặp không được kiểm soát được lập chỉ mục bởi k trong mã từ câu hỏi ban đầu. Nó cập nhật giá trị c tám lần bằng cách gọi macro C.

Giải pháp dựa trên bộ tiền xử lý này sử dụng khá nhiều bộ nhớ trong quá trình mở rộng macro. Tôi đã cố gắng làm cho nó ngắn hơn một chút bằng cách có thêm một mức độ mở rộng macro và trình biên dịch của tôi đã bị hỏng. Đoạn mã trên biên dịch (chậm) với cả Visual C ++ 2012 và g ++ 4.5.3 dưới Cygwin (RAM Windows 7 64 bit 8GB).

Biên tập:

Đoạn trên là 295 byte bao gồm cả khoảng trắng. Sau khi mở rộng tất cả các macro ngoại trừ C, nó tăng lên 9,918 byte. Khi mỗi cấp độ macro C được mở rộng, kích thước sẽ tăng lên nhanh chóng:

  1. 25.182
  2. 54.174
  3. 109,086
  4. 212.766
  5. 407.838
  6. 773.406
  7. 1.455.390
  8. 2.721.054

Vì vậy, tại thời điểm tất cả các macro đã được mở rộng, tệp 295 byte nhỏ đó sẽ mở rộng thành hơn 2,7 megabyte mã phải được biên dịch để tạo ra mảng 1024 byte ban đầu (giả sử các giá trị dài không dấu 32 bit)!

Chỉnh sửa khác:

Tôi đã sửa đổi macro C dựa trên một macro từ một câu trả lời khác để vắt thêm 11 byte và giảm đáng kể kích thước macro mở rộng đầy đủ. Mặc dù 2,7 MB không tệ bằng 54 MB (kích thước cuối cùng trước đó của tất cả các mở rộng macro), nhưng nó vẫn rất đáng kể.


Đây không phải là môn đánh gôn , vì vậy bạn không cần giảm thiểu số lượng ký tự.
Ilmari Karonen

Ah. Nên nó là. Xấu của tôi về phần đó Mặc dù tôi nghĩ rằng việc triển khai này là khả dụng (theo ý tôi là nó phù hợp với ngôn ngữ C và bộ tiền xử lý; tính di động thực sự của nó sẽ phụ thuộc vào giới hạn chính xác của môi trường đối với việc mở rộng macro).
CasaDeRobison

3

Tôi sẽ sửa đổi câu trả lời trước bằng cách thay thế ba dòng cuối cùng bằng:

#define crc4( x)    crcByte(x), crcByte(x+1), crcByte(x+2), crcByte(x+3)
#define crc16( x)   crc4(x), crc4(x+4), crc4(x+8), crc4(x+12)
#define crc64( x)   crc16(x), crc16(x+16), crc16(x+32), crc16(x+48)
#define crc256( x)  crc64(x), crc64(x+64), crc64(x+128), crc64(x+192)

Trong đó crcByte là macro K của anh ta mà không có dấu phẩy. Sau đó, xây dựng bảng với:

static const unsigned long crc32Table[256] = { crc256( 0)};

Và không bao giờ bỏ qua kích thước của mảng vì trình biên dịch sẽ xác minh rằng bạn có số lượng phần tử chính xác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.