前往
大廳
主題

Constexpr and Template Metaprogramming in C++11 Example2 - CRC32

Yang | 2023-07-16 07:47:48 | 巴幣 0 | 人氣 190


補充之前寫的,
樣板元编程(Template Metaprogramming)範例2 - CRC32雜湊碼

之前只能算出unsigned int
#define CRC2INT(x) (MM<sizeof(x) - 1>::CRC32(x))

這次新增
#define CRC2CARR(x) HexdecimalCArray<CRC2INT(x)>
能把unsigned int轉成16進位數值,並儲存在11個bytes的char array內

譬如CRC2CARR("1")為"0x83DCEFB7",字串長度10,
但陣列大小為11,因為結尾要多儲存'\0'代表字串結束

新增相關實際使用範例,以及StrLen和StrEqual,用來驗證char array方面的程式碼

//計算字串長度
constexpr size_t StrLen(const char *str)
{
    return *str ? 1 + StrLen(str + 1) : 0;
}

//比較兩個字串是否相同
constexpr bool StrEqual(char const *a, char const *b)
{
    return *a == *b && (*a == '\0' || StrEqual(a + 1, b + 1));
}

static constexpr char HexBaseCArray[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

template<char... Args>
struct HexMetaString
{
    int ContentSize = (sizeof... (Args));
    const char Data[sizeof... (Args)] = {Args...};
};

//利用遞迴,反覆對unsigned int除以16,取得商數和餘數,並且把餘數轉換成16進位碼(HexBaseCArray)
template<int Width, unsigned int N, char... Args>
struct HexdecimalCArrayBuilder
{
    typedef typename HexdecimalCArrayBuilder<Width - 1, N / 16, HexBaseCArray[N % 16], Args...>::Type Type;
};

template<unsigned int N, char... Args>
struct HexdecimalCArrayBuilder<0, N, Args...>
{
    typedef HexMetaString<'0', 'x', HexBaseCArray[N], Args...> Type;
};

template<unsigned int N>
class HexdecimalCArray
{
    private:
        typedef typename HexdecimalCArrayBuilder<8 - 1, N, '\0'>::Type t;

        static constexpr t metaStr {};

    public:
        static constexpr int GetContentSize()
        {
            return metaStr.ContentSize;
        }

        static constexpr const char *GetData()
        {
            return metaStr.Data;
        }

        static constexpr size_t GetStrLen()
        {
            return StrLen(metaStr.Data);
        }
};

template<unsigned int N>
constexpr typename HexdecimalCArray<N>::t HexdecimalCArray<N>::metaStr;

//CRC2CARR,計算輸入字串的CRC32,輸出成16進位數值,並儲存在11個bytes的char array內
#define CRC2CARR(x) HexdecimalCArray<CRC2INT(x)>

//計算和驗證CRC2CARR
//不用編譯和執行程式,滑鼠移到變數上,就能看到測試結果是否正常

//CRCCArrSize和CRCStrLen如果改成別的數字,相關的int值會得到-1,代表錯誤
//CRCCArrSizeXXX等於11代表正常
//CRCStrLenXXX等於10代表正常

//ChkCRCCArrXXX是用來比較和驗證,CRC2CARR算出來的16進位碼,
//和網站上算出來的是否相同,true代表相同,測試正常

static constexpr int CRCCArrSize = 11;
static constexpr int CRCStrLen = 10;

static constexpr const char *CRCCArrEmpty = CRC2CARR("")::GetData();
static constexpr int CRCCArrSizeEmpty = CRC2CARR("")::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLenEmpty = (CRC2CARR("")::GetStrLen() == CRCStrLen && StrLen(CRCCArrEmpty) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArrEmpty = StrEqual(CRCCArrEmpty, "0x00000000") && !StrEqual(CRCCArrEmpty, "0x0000000") && !StrEqual(CRCCArrEmpty, "0x00000000_");

static constexpr const char *CRCCArrSpace = CRC2CARR(" ")::GetData();
static constexpr int CRCCArrSizeSpace = CRC2CARR(" ")::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLenSpace = (CRC2CARR(" ")::GetStrLen() == CRCStrLen && StrLen(CRCCArrSpace) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArrSpace = StrEqual(CRCCArrSpace, "0xE96CCF45") && !StrEqual(CRCCArrSpace, "0xE96CCF4") && !StrEqual(CRCCArrSpace, "0xE96CCF45_");

static constexpr const char *CRCCArr1 = CRC2CARR("1")::GetData();
static constexpr int CRCCArrSize1 = CRC2CARR("1")::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLen1 = (CRC2CARR("1")::GetStrLen() == CRCStrLen && StrLen(CRCCArr1) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArr1 = StrEqual(CRCCArr1, "0x83DCEFB7") && !StrEqual(CRCCArr1, "0x83DCEFB") && !StrEqual(CRCCArr1, "0x83DCEFB7_");

static constexpr const char *CRCCArr123456789 = CRC2CARR("123456789")::GetData();
static constexpr int CRCCArrSize123456789 = CRC2CARR("123456789")::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLen123456789 = (CRC2CARR("123456789")::GetStrLen() == CRCStrLen && StrLen(CRCCArr123456789) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArr123456789 = StrEqual(CRCCArr123456789, "0xCBF43926") && !StrEqual(CRCCArr123456789, "0xCBF4392") && !StrEqual(CRCCArr123456789, "0xCBF43926_");

static constexpr const char *CRCCArrCrcVal01 = CRC2CARR("CrcVal01")::GetData();
static constexpr int CRCCArrSizeCrcVal01 = CRC2CARR("CrcVal01")::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLenCrcVal01 = (CRC2CARR("CrcVal01")::GetStrLen() == CRCStrLen && StrLen(CRCCArrCrcVal01) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArrCrcVal01 = StrEqual(CRCCArrCrcVal01, "0xCC3B0811") && !StrEqual(CRCCArrCrcVal01, "0xCC3B081") && !StrEqual(CRCCArrCrcVal01, "0xCC3B0811_");

static constexpr const char *CRCCArrFilePath = CRC2CARR(__FILE__)::GetData();
static constexpr int CRCCArrSizeFilePath = CRC2CARR(__FILE__)::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLenFilePath = (CRC2CARR(__FILE__)::GetStrLen() == CRCStrLen && StrLen(CRCCArrFilePath) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArrFilePath = StrEqual(CRCCArrFilePath, "0xB413D6BF") && !StrEqual(CRCCArrFilePath, "0xB413D6B") && !StrEqual(CRCCArrFilePath, "0xB413D6BF_");

對檔案路徑取CRC32 (CRC2CARR(__FILE__)),應用面可以用在之前寫的文章,譬如
C++11取得和轉換thread::id
C++物件的singleton constructor補充

程式內固定路徑的檔案或單一的物件(singleton class),能在編譯前就先給定獨一無二的CRC32,當作唯一碼

//擴充之前寫的ThreadExtensions
static constexpr const char *CRCCArrFilePathThreadExt = CRC2CARR(__FILE__)::GetData();
static constexpr int CRCCArrSizeFilePathThreadExt = CRC2CARR(__FILE__)::GetContentSize() == CRCCArrSize ? CRCCArrSize : -1;
static constexpr int CRCStrLenFilePathThreadExt = (CRC2CARR(__FILE__)::GetStrLen() == CRCStrLen && StrLen(CRCCArrFilePathThreadExt) == CRCStrLen) ? CRCStrLen : -1;
static constexpr bool ChkCRCCArrFilePathThreadExt = StrEqual(CRCCArrFilePathThreadExt, "0xD5166410") && !StrEqual(CRCCArrFilePathThreadExt, "0xD516641") && !StrEqual(CRCCArrFilePathThreadExt, "0xD5166410_");

參考

CRC32是很簡易的雜湊運算,每77164筆CRC32計算,就有50%機率重複(Birthday Problem),
因此在檔案很多的大型專案上,應該不太適合大量使用CRC2INT和CRC2CARR,避免衝突
送禮物贊助創作者 !
0
留言

創作回應

更多創作