0

I want to calculate a hash of the structure passing as string. Although vlanId values are different, the hash value is still the same. The StringHash() funtion calculates the values of the hash. I haven't assigned any value to portId and vsi.

#include<stdio.h>
#include <functional>
#include <cstring>

using namespace std;
unsigned long StringHash(unsigned char *Arr)
{
    hash<string> str_hash;

    string Str((const char *)Arr);

    unsigned long str_hash_value = str_hash(Str);
    printf("Hash=%lu\n", str_hash_value);

    return str_hash_value;
}

typedef struct 
{
    unsigned char portId;
    unsigned short vlanId;
    unsigned short vsi;
}VlanConfig; 

int main()
{
    VlanConfig v1;
    memset(&v1,0,sizeof(VlanConfig));
    unsigned char *index = (unsigned char *)&v1 + sizeof(unsigned char);    
        
    v1.vlanId = 10;
    StringHash(index);
    StringHash((unsigned char *)&v1);

    v1.vlanId = 12;
    StringHash(index);
    StringHash((unsigned char *)&v1);
    
    return 0;
}

Output:

Hash=6142509188972423790  
Hash=6142509188972423790  
Hash=6142509188972423790  
Hash=6142509188972423790
4
  • 2
    A debugger, a breakpoint on line string Str((const char *)Arr);, and an inspection window will tell you very quickly that this isn't the right way to hash VlanConfig. And not that it is necessarily related, " I haven't assigned any value to portId and vsi" - um. yes you did. what do you think that memset did? Commented Feb 25, 2021 at 9:16
  • 1
    StringHash((unsigned char*)&v1): you cast &v1 to a (unsigned char*) but &v1 is not a pointer to a string, it's a pointer to VlanConfig. The cast won't magically transform your struct into a string. It will just pretend &vi points to a string, but it doesn't. If you use casts in C++, you're doing solmething most of the time Commented Feb 25, 2021 at 9:24
  • Maybe because strings stop at a 0 byte? Commented Feb 25, 2021 at 9:25
  • You are hashing the string string Str((const char *)Arr); But you are building this like it is a C-String which is terminated by \0. I assume the first bye is zero and thus you are hashing an empty string. Build the string like this: string Str((const char *)Arr, <Size of Object being hasshed>); Note: I only recomend this to show why it is happening. You should do it properly as shown by @nvoigt below. Commented Feb 25, 2021 at 13:30

2 Answers 2

2

You pass the bytes of your structure to a function expecting a zero terminated string. Well, the first byte of your structure already is zero, so you calculate the same hash every time.

Now, that is the explanation why, but not the solution to your problem. Passing a random sequence of bytes to a function expecting a zero-terminated sequence of characters is going to fail spectacularly, no matter how you do it.

Find another way to hash your structure. You are already using hash<>, why not use it for your case:

namespace std
{
    template<> struct hash<VlanConfig>
    {
        std::size_t operator()(VlanConfig  const& c) const noexcept
        {
            std::size_t h1 = std::hash<char>{}(c.portId);
            std::size_t h2 = std::hash<short>{}(c.vlanId);
            std::size_t h3 = std::hash<short>{}(c.vsi);
            return h1 ^ (h2 << 1) ^ (h3 << 2); // or use boost::hash_combine
        }
    };
}

Then you can do this:

VlanConfig myVariable;

// fill myVariable

std::cout << std::hash<VlanConfig>{}(myVariable) << std::endl;
Sign up to request clarification or add additional context in comments.

Comments

-1

I can't say for certain, but most likely your issue is structure padding. Unless explicietly set ot pack members and ignore alignment, most compilers will set up the struct as follows:

Byte 0: portId
Byte 1: padding
Bytes 2,3: vlanId
Bytes 4,5: vsi

So when you figure the address of index, it'll point to the padding byte, which is always zero. Thus you're always hashing an empty string.

You should be able to check this in a debugger by inspecting index and comparing it to the address of vlanId.

-- Edit --

After giving this some more thought, I have to say that in my extremely humble opinion, this isn't a good way to get a hash value. Trying to treat several numeric values that might, or might not, be contiguous in memory as a std::string, has too many possibilities for error.

Start with the fact that even if you do get the address correct, consider what happens when you hash two different configurations, one of which has vlanId set to 256, while the other has it set to 512. Assuming a little endian machine, both of those will have a zero byte as the first character of the string, and so you're right back here again.

Worse yet is the case when all four bytes in vlanId and vsi are non zero. In that case, you'll read right off the end of your struct, and keep on going, reading who knows what. There's no way that's going to end well.

One possible solution is to figure the size of data, and use the following ctor for std::string: string (char const *s, size_t n); which has the advantage of forcing the string to exactly the size you want.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.