संकलन-समय पर CRC32 तालिका की गणना करें [बंद]


16

CRC32 का संदर्भ कार्यान्वयन रनटाइम पर लुकअप तालिका की गणना करता है:

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

क्या आप संकलन-समय पर तालिका की गणना कर सकते हैं, इस प्रकार फ़ंक्शन और स्थिति ध्वज से छुटकारा पा सकते हैं?


2
यहाँ उद्देश्य प्राथमिक जीत की कसौटी क्या है?
जॉन ड्वोरक

साथ ही, भाषा के विशिष्ट प्रश्नों को यहाँ प्रस्तुत किया गया है। आपको c ++ टैग को हटा देना चाहिए।
गर्वित हैकेलर

जवाबों:


12

यहाँ एक सादे सी समाधान है:

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

यह गैर-मानक __COUNTER__मैक्रो पर निर्भर करता है , साथ ही एक मूल्यांकन शब्दार्थ भी होता है जहां __COUNTER__मैक्रो के तर्क के रूप में पारित होने से पहले इसका मूल्यांकन किया जाता है।

ध्यान दें, चूंकि STEPदो बार इसके तर्क का मूल्यांकन किया गया है, और इसके CRCआठ नेस्टेड इनवोकेशन का उपयोग करता है, कोड आकार में एक छोटा दहनशील विस्फोट होता है:

$ cpp crc32table.c | wc -c
4563276

मैंने 32 बिट लिनक्स पर जीसीसी 4.6.0 और क्लैंग 2.8 में यह परीक्षण किया, और दोनों सही तालिका का उत्पादन करते हैं।


बहुत बढ़िया, मैं निश्चित रूप से यह उम्मीद नहीं कर रहा था। एक +1 है।
आर। मार्टिनो फर्नांडिस

9

कोर लूप

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

एक मेटा-फ़ंक्शन में परिवर्तित किया जा सकता है:

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

फिर, इस मेटा-फ़ंक्शन के लिए 256 कॉल (सरणी इनिशियलाइज़र के लिए) प्रीप्रोसेसर द्वारा जेनरेट की जाती हैं:

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

यदि आपके पास बूस्ट स्थापित है, तो ऐरर इनिशियलाइज़र बनाना थोड़ा सरल है:

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

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

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

अंत में, निम्न परीक्षण चालक कंसोल के लिए सभी सरणी तत्वों को प्रिंट करता है:

#include <cstdio>

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

6

एक 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;
}

जीसीसी (4.6.1) और क्लैंग (ट्रंक 134121) पर काम करता है।


के बारे में C >> 1, क्या अनिर्दिष्ट व्यवहार के लिए नकारात्मक मूल्यों को स्थानांतरित नहीं किया जा रहा है? ;)
fredoverflow

इसके अलावा, क्या आप उस हिस्से की व्याख्या कर सकते हैं जहाँ आप सरणी को परिभाषित करते हैं? मैं वहाँ थोड़ा खो गया हूँ ...
fredoverflow

@ अगर आप सही हैं। मैं भी करेगा Cएक unsigned long। निरंतर सरणी को पैक विस्तार द्वारा आरंभिक रूप से परिभाषित किया गया है D...Dएक गैर-प्रकार टेम्पलेट पैरामीटर पैक है। एक बार जीसीसी इसका समर्थन करता है, तो कोई भी श्रेणी के साथ सरणी की घोषणा कर सकता है static unsigned long constexpr crc_table[] = { D... };, लेकिन जीसीसी अभी तक इन-क्लास इनिशियलाइजर्स के ब्रेड को पार्स नहीं करता है। लाभ यह होगा कि compute<>::crc_table[I]कोड में निरंतर अभिव्यक्तियों के बाद उपयोग किया जा सकता है।
जोहान्स स्काउब -

5

C ++ 0x के साथ constexpr। 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;
}

इसके बाद आप उपयोग कर सकते हैं crc_table.data[X]संकलन समय पर क्योंकि crc_tableहै constexpr


4

यह मेरा पहला रूपक है :

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

मैं कॉम्पेक्ट करने वाले टेम्पलेट को कॉल "हार्डकोड" करता हूं :)


1
यदि आप इसे इस तरह करने जा रहे हैं, तो आप प्रोग्राम में वास्तविक मानों को हार्डकोड क्यों नहीं करेंगे? (उन्हें दूसरी विधि के साथ प्राप्त करने के बाद, निश्चित रूप से।) संकलन समय पर बचाता है।
मैथ्यू पढ़ें

परीक्षण और timesटेम्पलेट के लिए +1
fredoverflow

@ मैथ्यू: केवल सी ++ 03 के साथ, बहुत कम विकल्प है। आप प्रीप्रोसेसर का उपयोग कर सकते हैं, जैसे फ्रेड ने किया था, लेकिन यह संकलन समय को छोटा नहीं करेगा। और जाहिर है, उनके पूर्वप्रतिपादक उनके समाधान पर चुटकी लेते हैं :)
आर। मार्टिनो फर्नांडीस

@ मैथ्यू बिंदु वास्तव में संकलन-समय पर इसकी गणना करना है , न कि उन्हें हार्डकोड करना। फ्रेड का जवाब इस फॉर्म की एक सरणी उत्पन्न करता है: 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 ,प्रीप्रोसेसर का उपयोग करना। मुझे मेरा संकलन करने में जितना समय लगता है। यदि आप चाहें, तो आप उस अंतिम पैराग्राफ को "मैं बाहरी लूप को अनियंत्रित" कर सकते हैं। C ++ 03
R. मार्टिनहो फर्नांडिस

आह, मैं प्रश्न की आवश्यकताओं पर पर्याप्त ध्यान नहीं दे रहा था। +1, हालांकि मुझे यकीन नहीं है कि मुझे अब सवाल पसंद है। मुझे अपना कोड व्यावहारिक लगता है: P
मैथ्यू पढ़ें

3

डी

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

यह वास्तव में शर्म करने के लिए C ++ रखता है, है ना?


2
वास्तव में, मैं गैर-कड़े-टाइप किए गए समाधानों को पसंद करता हूं। यह संकलन-समय की तरह लग रहा है eval
आर। मार्टिनो फर्नांडिस

इसके लिए एक स्ट्रिंग मिक्सिन का उपयोग करने की आवश्यकता नहीं है, यहां है कि हम इसे डी के मानक पुस्तकालय, जीनटैबल्स और कॉल-साइट में कैसे करते हैं ताकि परिणाम का डेटा सेगमेंट में स्टोर किया जा सके।
मार्टिन नोवाक

3

सी / सी ++, 306 295 बाइट्स

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

रिवर्स में काम करते हुए, हम crc_table नाम के एक अहस्ताक्षरित लंबे सरणी के साथ हवा करते हैं। हम सरणी के आकार को छोड़ सकते हैं क्योंकि मैक्रो सुनिश्चित करेगा कि सरणी में बिल्कुल 256 तत्व हैं। हम मैक्रो आर के 16 चालान का उपयोग करके डेटा की 16 'पंक्तियों' के साथ सरणी को आरंभीकृत करते हैं।

R का प्रत्येक आह्वान डेटा के कुल 16 'कॉलम' के लिए चार स्थिरांक (मैक्रो K) के चार टुकड़ों (मैक्रो F) में फैलता है।

मैक्रो K मूल प्रश्न से कोड में k द्वारा अनुक्रमित अनियंत्रित लूप है। यह मैक्रो C को दबाकर आठ बार मान c को अद्यतन करता है।

यह प्रीप्रोसेसर आधारित समाधान मैक्रो विस्तार के दौरान स्मृति का काफी उपयोग करता है। मैंने मैक्रो विस्तार के एक अतिरिक्त स्तर और मेरे कंपाइलर को पुक करके इसे थोड़ा कम करने की कोशिश की। विजुअल C ++ 2012 और सिग्विन (विंडोज 7 64 बिट 8 जीबी रैम) के तहत विजुअल C ++ 2012 और जी ++ 4.5.3 दोनों के साथ कोड ऊपर (धीरे)।

संपादित करें:

सफेद स्थान सहित 295 बाइट्स के ऊपर का टुकड़ा है। सी को छोड़कर सभी मैक्रोज़ का विस्तार करने के बाद यह 9,918 बाइट्स तक बढ़ता है। C मैक्रो के प्रत्येक स्तर का विस्तार होने के कारण आकार जल्दी बढ़ता है:

  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

इसलिए जब तक सभी मैक्रोज़ का विस्तार नहीं किया गया, तब तक कि 295 295 बाइट फ़ाइल 2.7 मेगाबाइट से अधिक कोड में फैल जाती है, जिसे मूल 1024 बाइट सरणी (32 बिट अहस्ताक्षरित लंबे मान मान) उत्पन्न करने के लिए संकलित किया जाना चाहिए!

एक और संपादन:

मैंने एक अतिरिक्त 11 बाइट को निचोड़ने के लिए एक अन्य उत्तर से एक मैक्रो पर आधारित सी मैक्रो को संशोधित किया, और पूर्ण विस्तारित मैक्रो आकार को बहुत कम कर दिया। जबकि 2.7 एमबी 54 एमबी (सभी मैक्रो विस्तार का पिछला अंतिम आकार) जितना बुरा नहीं है, यह अभी भी महत्वपूर्ण है।


यह कोड-गोल्फ नहीं है , इसलिए आपको वर्ण गणना को न्यूनतम करने की आवश्यकता नहीं है।
इल्मरी करोनें

आह। इसलिए यह। उस हिस्से पर मेरा बुरा। हालांकि मुझे लगता है कि यह कार्यान्वयन पोर्टेबल है (जिसके द्वारा मेरा मतलब है कि यह सी भाषा और प्रीप्रोसेसर के अनुरूप है; इसकी सही पोर्टेबिलिटी मैक्रो विस्तार पर पर्यावरण की सटीक सीमाओं पर निर्भर करेगी)।
कासाडेयोरसन

3

मैं अंतिम उत्तर को अंतिम तीन पंक्तियों के साथ बदलकर संशोधित करूंगा:

#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)

जहां crcByte अपने K मैक्रो के पीछे पीछे चल रहा अल्पविराम है। इसके बाद तालिका को स्वयं बनाएं:

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

और सरणी के आकार को कभी न छोड़ें क्योंकि कंपाइलर यह सत्यापित करेगा कि आपके पास तत्वों की सही मात्रा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.