Hachage de chaîne au moment de la compilation

100

J'ai lu à quelques endroits différents qu'en utilisant les nouveaux littéraux de chaîne de C ++ 11, il pourrait être possible de calculer le hachage d'une chaîne au moment de la compilation. Cependant, personne ne semble prêt à sortir et à dire que ce sera possible ou comment cela se ferait.

  • Est-ce possible?
  • À quoi ressemblerait l'opérateur?

Je suis particulièrement intéressé par des cas d'utilisation comme celui-ci.

void foo( const std::string& value )
{
   switch( std::hash(value) )
   {
      case "one"_hash: one(); break;
      case "two"_hash: two(); break;
      /*many more cases*/
      default: other(); break;
   }
}

Remarque: la fonction de hachage au moment de la compilation n'a pas besoin d'être exactement telle que je l'ai écrite. J'ai fait de mon mieux pour deviner à quoi ressemblerait la solution finale, mais meta_hash<"string"_meta>::valuepourrait aussi être une solution viable.

code_deft
la source
Je n'arrive pas à trouver quoi que ce soit non plus, mais je pourrais voir devoir forcer votre fonction de hachage dans une constexpr.
luke
4
Existe-t-il un compilateur qui prend déjà en charge les littéraux définis par l'utilisateur? Gcc ne le fait pas ( gcc.gnu.org/projects/cxx0x.html ) et je ne les ai pas trouvés non plus mentionnés pour VC10. Sans la prise en charge du compilateur, cela ne peut être que supposé, mais les littéraux définis par l'utilisateur ont l' air d' être possibles.
Georg Fritzsche
1
C'est mignon mais pas utile? Si la valeur du commutateur est une chaîne d'exécution, vous devez également rechercher les collisions. Peut-être que l'emballage est meilleur (ma réponse a une fonction de pack pour remplir 9 caractères en 64 bits).
u0b34a0f6ae
@ u0b34a0f6ae Pourquoi rechercher des collisions?
cubuspl42
Le compilateur doit émettre une erreur si deux valeurs de cas sont égales.
Mark Storer

Réponses:

88

C'est un peu tard, mais j'ai réussi à implémenter une fonction CRC32 à la compilation avec l'utilisation de constexpr . Le problème avec cela est qu'au moment de la rédaction de cet article, il ne fonctionne qu'avec GCC et non avec le compilateur MSVC ou Intel.

Voici l'extrait de code:

// CRC32 Table (zlib polynomial)
static constexpr uint32_t crc_table[256] = {
    0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
    0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
    0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
...
};
template<size_t idx>
constexpr uint32_t crc32(const char * str)
{
    return (crc32<idx-1>(str) >> 8) ^ crc_table[(crc32<idx-1>(str) ^ str[idx]) & 0x000000FF];
}

// This is the stop-recursion function
template<>
constexpr uint32_t crc32<size_t(-1)>(const char * str)
{
    return 0xFFFFFFFF;
}

// This doesn't take into account the nul char
#define COMPILE_TIME_CRC32_STR(x) (crc32<sizeof(x) - 2>(x) ^ 0xFFFFFFFF)

enum TestEnum
{
    CrcVal01 = COMPILE_TIME_CRC32_STR("stack-overflow"),
};

CrcVal01 est égal à 0x335CC04A

J'espère que ceci vous aidera!

Clément JACOB
la source
4
Oui absolument. Je l'ai testé contre l'algorithme d'exécution Python CRC32 (provenant de zlib) et les résultats sont les mêmes. En fait, ce que vous essayez de réaliser est exactement la raison pour laquelle j'utilise cette technique!
Clement JACOB
2
Merci d'avoir publié ceci, c'est très utile!
Tamás Szelei
2
Il vous manquait un indicateur de compilation. De plus, j'ai dû convertir en size_t la valeur -1 dans la fonction de modèle d'arrêt de récursivité. La version mise à jour est disponible ici (travaillant à partir de Clang 3.3): goo.gl/vPMkfB
Clement JACOB
1
constexprn'est pas disponible dans VS2013, sauf en novembre 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/…
2
Vous revenez deux fois. Cette solution a une complexité exponentielle par rapport à la longueur de la chaîne, et le compilateur n'est pas assez intelligent pour élider le deuxième appel. Vérifiez les autres réponses pour une solution possible à ce problème.
CygnusX1
30

Au moins d'après ma lecture des §7.1.5 / 3 et §5.19, ce qui suit pourrait être légitime:

unsigned constexpr const_hash(char const *input) {
    return *input ?
        static_cast<unsigned int>(*input) + 33 * const_hash(input + 1) :
        5381;
}

Cela semble suivre les règles de base du §7.1.5 / 3:

  1. La forme est: "expression de retour;"
  2. Son seul paramètre est un pointeur, qui est un type scalaire, et donc un type littéral.
  3. Son retour est unsigned int, qui est également scalaire (et donc littéral).
  4. Il n'y a pas de conversion implicite vers le type de retour.

On se demande si les *inputs impliquent une conversion illégale de lvalue en rvalue, et je ne suis pas sûr de comprendre les règles du §5.19 / 2/6/2 1 et du §4.1 pour être sûr de cela.

D'un point de vue pratique, ce code est accepté par (pour un exemple) g ++, au moins aussi loin que g ++ 4.7.1.

L'utilisation serait quelque chose comme:

switch(std::hash(value)) {
    case const_hash("one"): one(); break;
    case const_hash("two"): two(); break;
    // ...
    default: other(); break;
}

Pour vous conformer aux exigences du §5.19 / 2/6/2, vous devrez peut-être faire quelque chose comme ceci:

// one of the `constexpr`s is probably redundant, but I haven't figure out which.
char constexpr * constexpr v_one = "one"; 

// ....

case const_hash(v_one): one(); break;
  1. J'utilise les numéros supplémentaires `` barre oblique '' pour faire référence à des puces non numérotées, c'est donc le deuxième point à l'intérieur si le sixième point sous §5.19 / 2. Je pense que je devrais peut-être parler à Pete Becker pour savoir s'il est possible d'ajouter une sorte de chiffres / lettres / chiffres romains dans la hiérarchie pour identifier des pièces comme celle-ci ...
Jerry Coffin
la source
3
Deux choses ne vont pas avec ça. 1: Les appels récursifs ne sont pas autorisés constexpr, 2: Vous n'avez pas de condition d'arrêt (où *input == nullptr) et si je comprends bien, constexprvous ne pouvez pas en avoir.
Motti
9
Où est-il dit que la récursivité n'est pas autorisée dans une constexpr? Pour autant que je sache, il est seulement dit que toutes les fonctions que vous appelez doivent elles-mêmes être marquées constexprt (§5.19 / 2/2). J'ai fait une erreur dans la condition de résiliation, que j'ai maintenant corrigée (j'ai accidentellement utilisé || là où il aurait dû être &&).
Jerry Coffin
3
constexpr récursif. J'ai lu quelques procès-verbaux de réunion de 2008. Il y a eu une discussion sur l'autorisation ou l'interdiction des fonctions constexpr récursives. L'essentiel était que la formulation actuelle ne l'interdisait pas et l'impliquait légèrement. Le comité a estimé qu'une caractéristique aussi puissante devrait être explicitement énoncée. C'était en 2008, je ne sais pas ce qui s'est passé depuis.
deft_code
3
Droite - j'aurais probablement dû souligner que j'allais à partir de N3000, qui (je crois) est actuellement le projet le plus récent. Je suis à peu près sûr que cela a été interdit à un moment donné, mais au moins pour l'instant, cela semble être autorisé.
Jerry Coffin le
2
Um, l'opérateur && renvoie un booléen, donc cette fonction ne fait probablement pas ce que vous voulez. En particulier, il renvoie 0 pour toute chaîne vide et éventuellement certaines autres chaînes commençant par un caractère qui se convertit en (unsigned)-1s'il y en a; et renvoie 1 pour toutes les autres chaînes. Réécrire avec un opérateur conditionnel ternaire?
ndkrempel
13

Il s'agit d'une tentative de résoudre le problème du PO aussi exactement que possible.

namespace my_hash {
  template<class>struct hasher;
  template<>
  struct hasher<std::string> {
    std::size_t constexpr operator()(char const *input)const {
      return *input ?
        static_cast<unsigned int>(*input) + 33 * (*this)(input + 1) :
        5381;
    }
    std::size_t operator()( const std::string& str ) const {
      return (*this)(str.c_str());
    }
  };
  template<typename T>
  std::size_t constexpr hash(T&& t) {
    return hasher< typename std::decay<T>::type >()(std::forward<T>(t));
  }
  inline namespace literals {
    std::size_t constexpr operator "" _hash(const char* s,size_t) {
      return hasher<std::string>()(s);
    }
  }
}
using namespace my_hash::literals;
void one() {} void two() {} void other() {}

void foo( const std::string& value )
{
  switch( my_hash::hash(value) )
  {
    case "one"_hash: one(); break;
    case "two"_hash: two(); break;
    /*many more cases*/
    default: other(); break;
  }
}

exemple en direct .

Notez la différence principale - std::hashne peut pas être utilisée, car nous n'avons pas de contrôle sur std::hashl'algorithme de s, et nous devons le réimplémenter en tant que constexprafin de l'évaluer au moment de la compilation. De plus, il n'y a pas de hachage "transparent" std, vous ne pouvez donc pas (sans créer a std::string) hacher un tampon de caractères bruts en tant que fichier std::string.

J'ai collé le std::stringhachage personnalisé (avec const char*prise en charge transparente ) dans un my_hashespace de noms, vous pouvez donc le stocker dans un std::unordered_mapsi vous avez besoin de cohérence.

Basé sur l'excellente réponse de @ JerryCoffin et le fil de commentaires ci-dessous, mais avec une tentative de l'écrire avec les meilleures pratiques actuelles de C ++ 11 (au lieu de les anticiper!).

Notez que l'utilisation d'un "hachage brut" pour une switchinstruction caseest dangereuse. Vous voudrez faire une ==comparaison par la suite pour confirmer que cela a fonctionné.

Yakk - Adam Nevraumont
la source
2
Le lien d'exemple en direct semble être incorrect / obsolète?
fuenfundachtzig
@fuenfundachtzig Croiriez-vous que je viens de le réparer?
Yakk - Adam Nevraumont
13

Cet extrait est basé sur celui de Clement JACOB. Mais fonctionne aussi avec clang. Et cela devrait être plus rapide à la compilation (il n'a qu'un seul appel récursif, pas deux comme dans le post original).

#include <iostream>
#include <string>
#include <vector>

static constexpr unsigned int crc_table[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3,    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};


template<int size, int idx = 0, class dummy = void>
struct MM{
  static constexpr unsigned int crc32(const char * str, unsigned int prev_crc = 0xFFFFFFFF)
  {
      return MM<size, idx+1>::crc32(str, (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF] );
  }
};

// This is the stop-recursion function
template<int size, class dummy>
struct MM<size, size, dummy>{
  static constexpr unsigned int crc32(const char * str, unsigned int prev_crc = 0xFFFFFFFF)
  {
      return prev_crc^ 0xFFFFFFFF;
  }
};

// This don't take into account the nul char
#define COMPILE_TIME_CRC32_STR(x) (MM<sizeof(x)-1>::crc32(x))


template<unsigned int crc>
void PrintCrc()
{
    std::cout << crc << std::endl;
}


int main()
{

    PrintCrc<COMPILE_TIME_CRC32_STR("HAH")>();
}

Voir la preuve de concept ici

tour120
la source
1
Merci, la réponse de Jacob fonctionne bien pour ce que je voulais sur GCC mais msvc lançait des erreurs avec des chaînes plus grandes. Votre réponse fonctionne sur msvc avec les chaînes plus grandes dont j'ai besoin pour hacher.
Daniel Moodie
8

Notez que le formulaire présenté ici n'a pas été accepté dans la norme, comme indiqué ci-dessous.

Le traitement des chaînes de temps de compilation est supposé devenir possible grâce aux littéraux définis par l' utilisateur proposés dans N2765 .
Comme je l'ai déjà mentionné, je ne connais aucun compilateur qui l'implémente actuellement et sans le support du compilateur, il ne peut y avoir que des suppositions.

Aux §2.13.7.3 et 4 du projet, nous avons ce qui suit:

Sinon (S contient un modèle d'opérateur littéral), L est traité comme un appel de l'
opérateur de formulaire "" X <'c1', 'c2', ..., 'ck'> () où n est la séquence de caractères source c1c2 ... ck. [Remarque: la séquence c1c2 ... ck ne peut contenir que des caractères du jeu de caractères source de base. —End note]

Combinez cela avec constexpret nous devrions avoir un traitement de chaîne de compilation.

mise à jour: j'ai oublié que je lisais le mauvais paragraphe, ce formulaire est autorisé pour les littéraux entiers définis par l'utilisateur et les littéraux flottants, mais apparemment pas pour les littéraux-chaîne (§2.13.7.5).
Cette partie de la proposition ne semble pas avoir été acceptée.

Cela étant dit, avec mon aperçu limité de C ++ 0x, cela pourrait ressembler à quelque chose comme ça (j'ai probablement eu un problème):

template<char c, char... str>
struct hash {
    static const unsigned result = c + hash<str...>::result;
};

template<char c>
struct hash {
    static const unsigned result = c;
};

template<char... str> 
constexpr unsigned
operator "" _hash() {
    return hash<str>::result;
}

// update: probably wrong, because the above 
// form is not allowed for string-literals:    
const unsigned h = "abcd"_hash;

Si l' approche Jerrys fonctionne, les éléments suivants devraient cependant fonctionner:

constexpr unsigned operator "" _hash(const char* s, size_t) {
    return const_hash(s);
}
Georg Fritzsche
la source
Belle (et nécessaire) combinaison de modèles de longueurs variables et de constexprlittéraux définis par l'utilisateur. Je ne suis pas sûr que vous puissiez utiliser une chaîne littérale comme paramètre de modèle, n'ont-ils pas une liaison statique? (ils le font au moins en C ++ 98 et sont donc verboten comme paramètres de modèle).
Motti
J'ai mélangé les paragraphes et j'ai pensé que ce cas était une exception - malheureusement, cela ne semble pas être le cas.
Georg Fritzsche
1
@Motti: où gf utilise-t-il la chaîne littérale comme paramètre de modèle? Êtes-vous confus en passant le modèle variadique de caractères en tant que chaîne littérale?
deft_code
Il semble que d'après la proposition originale, la version du modèle pour les chaînes littérales n'a pas été acceptée (en raison de problèmes de concaténation? Stackoverflow.com/questions/1108008/any-ideas-for-c1y/... - commentaires) - dommage.
Georg Fritzsche
1
La dernière version de operator ""_hashfonctionne pour moi dans Xcode 5.0.2.
cubuspl42
8

Une autre solution basée sur celle de Clement JACOB, utilisant C ++ 11 constexpr (pas le C ++ 14 étendu) mais n'ayant qu'une seule récursivité.

namespace detail {
// CRC32 Table (zlib polynomial)
static constexpr uint32_t crc_table[256] = { 0x00000000L, 0x77073096L, ... }

template<size_t idx>
constexpr uint32_t combine_crc32(const char * str, uint32_t part) {
  return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
}

template<size_t idx>
constexpr uint32_t crc32(const char * str) {
  return combine_crc32<idx>(str, crc32<idx - 1>(str));
}

// This is the stop-recursion function
template<>
constexpr uint32_t crc32<size_t(-1)>(const char * str) {
  return 0xFFFFFFFF;
}

} //namespace detail

template <size_t len>
constexpr uint32_t ctcrc32(const char (&str)[len]) {
  return detail::crc32<len - 2>(str) ^ 0xFFFFFFFF;
}

Quelques explications

  • Nous utilisons une seule récursivité, de sorte que la fonction fonctionne bien même pour des chaînes plus longues.
  • La fonction supplémentaire combine_crc32nous permet de stocker le résultat d'une récursion sous une variablepart et de l'utiliser deux fois. Cette fonction est une solution pour la limitation C ++ 11 interdisant les déclarations de variables locales.
  • La ctcrc32fonction attend un littéral de chaîne, qui est passé en tant que const char (&)[len]. De cette façon, nous pouvons obtenir la longueur de la chaîne en tant que paramètre de modèle et ne pas avoir à compter sur des macros.
CygnusX1
la source
2
Cela a fini par être ma mise en œuvre préférée, merci.
Daniel Moodie
6

Ce qui suit fonctionne dans GCC 4.6.1, et vous pouvez utiliser l'un hashou l' autre des packblocs de commutation.

/* Fast simple string hash (Bernstein?) */                                       
constexpr unsigned int hash(const char *s, int off = 0) {                        
    return !s[off] ? 5381 : (hash(s, off+1)*33) ^ s[off];                           
}                                                                                

/* Pack the string into an unsigned int                                          
 * Using 7 bits (ascii) it packs 9 chars into a uint64_t                         
 */                                                                              
template <class T = uint64_t, unsigned int Bits = 7>                             
constexpr T pack(const char *s, unsigned int off = 0) {                          
    return (Bits*off >= CHAR_BIT*sizeof(T) || !s[off]) ? 0 :                     
        (((T)s[off] << (Bits*off)) | pack(s,off+1));                             
}  

GCC apparemment (?) Ne permet pas les appels récursifs où nous passons s+1avec sun pointeur, c'est pourquoi j'utilise la offvariable.

u0b34a0f6ae
la source
5

Si vous avez un compilateur c ++ 17 et string_view, cela devient trivial, écrivez simplement la version normale:

constexpr uint32_t crc32(std::string_view str)
{
    uint32_t crc = 0xffffffff;
    for (auto c : str)
        crc = (crc >> 8) ^ crc_table[(crc ^ c) & 0xff];
    return crc ^ 0xffffffff;
}
Guillaume Gris
la source
Notez que le compilateur peut décider de ne pas traiter cela au moment de la compilation si vous écrivez simplement crc32("mystring")(généralement VS a tendance à le faire). L'astuce pour contourner ce problème consiste à créer une variable constexpr qui dépend de l'évaluation de la compilation de votre crc32. Typiquementconstexpr uint32_t val = crc32("mystring");
Guillaume Gris
3

Voici une autre implémentation C ++ 11 (basée sur la réponse @ CygnusX1), qui fonctionne à la fois avec les tableaux de caractères constexpr et les chaînes d'exécution:

namespace detail {

    // CRC32 Table (zlib polynomial)
    static constexpr uint32_t crc_table[256] = { 0x00000000L, 0x77073096L, ... };

    constexpr uint32_t combine_crc32(size_t idx, const char * str, uint32_t part) {
        return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
    }

    constexpr uint32_t crc32(size_t idx, const char * str) {
        return idx == size_t(-1) ? 
            0xFFFFFFFF : combine_crc32(idx, str, crc32(idx - 1, str));
    }
}

uint32_t ctcrc32(std::string const& str) {
    size_t len = str.size() + 1;
    return detail::crc32(len - 2, str.c_str()) ^ 0xFFFFFFFF;
}

template <size_t len>
constexpr uint32_t ctcrc32(const char (&str)[len]) {
    return detail::crc32(len - 2, str) ^ 0xFFFFFFFF;
}

Vous avez besoin str.size() + 1 parce que lendans la deuxième surcharge eststrlen(str) + 1 due au caractère nul à la fin.

Je n'ai pas ajouté de surcharge pour const char *car cela gâche la deuxième surcharge - Vous pouvez facilement ajouter des surcharges pour const char *, size_tou std::string_view.

Holt
la source
2

C'est une bonne question.

Sur la base de la réponse de Jerry Coffin, j'en ai créé un autre compatible avec std :: hash de Visual Studio 2017.

#include <functional>
#include <cassert>
using namespace std;


constexpr size_t cx_hash(const char* input) {
    size_t hash = sizeof(size_t) == 8 ? 0xcbf29ce484222325 : 0x811c9dc5;
    const size_t prime = sizeof(size_t) == 8 ? 0x00000100000001b3 : 0x01000193;

    while (*input) {
        hash ^= static_cast<size_t>(*input);
        hash *= prime;
        ++input;
    }

    return hash;
}


int main() {
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */

    auto a = cx_hash("test");
    hash<string> func;
    auto b = func("test");
    assert(a == b);

    return 0;
}

https://github.com/manuelgustavo/cx_hash

manuel saraiva
la source
0

Il me manquait toujours une variante crc32-littérale (ce qui n'est pas possible avec les modèles), voici donc ma suggestion basée sur CygnusX1 . Avez-vous fait des tests, n'hésitez pas à donner votre avis.

Testet sur MSVC.

PS: Je déteste chercher des trucs supplémentaires ailleurs, alors j'ai copié le tableau CRC au bas de ma réponse.

#include <inttypes.h>

namespace detail
{
    // CRC32 Table (zlib polynomial)
    static constexpr uint32_t crc_table[256] =
    {
        0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
        ...
    };

    constexpr uint32_t combine_crc32( const char c, uint32_t part )
    {
        return (part >> 8) ^ crc_table[(part ^ c) & 0x000000FF];
    }

    constexpr uint32_t crc32( const char * str, size_t idx )
    {
        return combine_crc32( str[idx], idx ? crc32( str, idx - 1 ) : 0xFFFFFFFF );
    }
} //namespace detail

constexpr uint32_t ctcrc32( const char* str, size_t len )
{
    return detail::crc32( str, len ) ^ 0xFFFFFFFF;
}

size_t constexpr operator "" _hash( const char* str, size_t len )
{
    return ctcrc32( str, len );
}

Alternative avec algorithmn de Dan Bernstein (djb2) (réponses combinées de Jerry Coffin + Georg Fritzsche )

unsigned constexpr const_hash( char const *input )
{
    return *input ?
        static_cast<unsigned int>(*input) + 33 * const_hash( input + 1 ) :
        5381;
}
size_t constexpr operator "" _hash( const char* str, size_t len )
{
    return const_hash( str );
}

Tableau CRC32:

static constexpr uint32_t crc_table[256] =
    {
        0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
        0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
        0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
        0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
        0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
        0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
        0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
        0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
        0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
        0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
        0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
        0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
        0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
        0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
        0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
        0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
        0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
        0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
        0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
        0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
        0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
        0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
        0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
        0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
        0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
        0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
        0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
        0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
        0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
        0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
        0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
        0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
        0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
        0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
        0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
        0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
        0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
        0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
        0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
        0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
        0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
        0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
        0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
        0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
        0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
        0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
        0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
        0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
        0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
        0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
        0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
        0x2d02ef8dL
    };
Zacharias
la source