Question

I have the following method which should create a unique long for any combination of the provided params:

private static long GenerateKey(byte sT, byte srcT, int nId, ushort aId)
{
    var nBytes = BitConverter.GetBytes(nId);
    var aBytes = BitConverter.GetBytes(aId);

    var byteArray = new byte[8];

    Buffer.BlockCopy(new[] { sT}, 0, byteArray, 7, 1);
    Buffer.BlockCopy(new[] { srcT}, 0, byteArray, 6, 1);
    Buffer.BlockCopy(aBytes, 0, byteArray, 4, 2);
    Buffer.BlockCopy(nBytes, 0, byteArray, 0, 4);


    var result = BitConverter.ToInt64(byteArray, 0);

    return result;
}

So I end up with:

   1    2   3   4    5   6   7   8    
------------------------------------
|byte|byte| ushort |      int      |
------------------------------------

Can this be done with bitwise operations? I tried the below but seemed to generate the same number for different values??

var hash = ((byte)sT << 56) ^ ((byte)srcT<< 48) ^ (aId << 32) ^ nId;
Was it helpful?

Solution

What went wrong here is that something like this

((byte)sT << 56)

does not do what you want. What it actually does, is cast sT to a byte (which it already is), then it's implicitly converted to an int (because you do math with it), and then it's shifted left by 24 (shift counts are masked to limit them to less than the size of the left operand).

So actually instead of casting to a narrow type, you should cast to a wide type, like this:

((long)sT << 56)

Also, note that implicitly converting an int to long sign-extends it, which means that if nId is negative, you would end up complementing all the other fields (xor with all ones is complement).

So try this:

((long)sT << 56) | ((long)srcT << 48) | ((long)aId << 32) | (nId & 0xffffffffL)

I've also changed the xor's to or's, it's more idiomatic for combining non-overlapping things.

OTHER TIPS

In the following line:

var hash = ((byte)sT << 56) ^ ((byte)srcT<< 48) ^ (aId << 32) ^ nId;

You are shifting more bits than the types are capable of holding. You can't shift a byte more than 8 bits, the rest is lost. Conversely, an ushort cannot be shifted more than 16 bits. Just cast everything to long and you'll get the expected result:

var hash = ((long)sT << 56)
         ^ ((long)srcT << 48)
         ^ ((long)aId << 32)
         ^ (uint)nId; // Cast to uint is required to prevent sign extension
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top