Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Treat visual representation of integer as hexadecimal to place in guid

Tags:

c#

.net

We have a legacy requirement to store what are now newly migrated int ID values into a guid type for use on ID-agnostic data types (basically old code that took advantage of the "globally unique" part of guid in order to contain all possible IDs in one column/field).

Due to this requirement, there was a follow-on requirement to embed the integer ID of entites into guid in a human-readable manner. This is important and is currently what is stopping me from working against the byte values directly.

Currently, I have the following:

    public static byte[] IntAsHexBytes(int value)
    {
        return BitConverter.GetBytes(Convert.ToInt64(value.ToString(), 16));
    }

    public static Guid EmbedInteger(int id)
    {
        var ib = IntAsHexBytes(id);

        return new Guid(new byte[]
            {
                0,0,0,0,0,0,1,64,ib[7],ib[6],ib[5],ib[4],ib[3],ib[2],ib[1],ib[0]
            });
    }

It treats the visual representation of the int as a hex value (value.ToString()), converts that to a long (Convert.ToInt64(value.ToString(), 16)) and the grabs the bytes from the long into a flattened byte[] for creating a guid in a particular structure.

So given an int of 42, when you treat 42 as a hex and convert that to an long you get 66, and on to the bytes of 66 gives, placing into a guid gives:

"00000000-0000-4001-0000-000000000042"

And an int of 379932126 gives:

"00000000-0000-4001-0000-000379932126"

So the end result is to place the integer into the guid in the last 12 digits so it visually looks like the integer 42 (even though the underlying integer value was 66).

This is roughly 30%-40% faster than constructing a string using concatenation in order to feed into the new Guid(string) constructor, but I feel I'm missing the solution that avoids having to do anything with strings in the first place.

The actual timings involved are quite small so as a performance improvement it probably won't justify the effort.

This is purely for the sake of my own curiosity to see if there are faster ways of tackling this problem. I posted here as I'm a long-standing SO user, but I'm torn as to whether this is a code-review-ish question, though I'm not asking for anything against my code directly, it just demonstrates what I want as output.

The integer range being supplied is 0 to int.MaxValue.

Update: For completeness, this is what we currently have and what I'm testing against:

string s = string.Format("00000000-0000-4001-0000-{0:D12}", id);
return new Guid(s);

My other code above is faster than this by around 30%.

like image 467
Adam Houldsworth Avatar asked Nov 03 '22 19:11

Adam Houldsworth


1 Answers

Ok, here's another version which completely avoids strings. Hopefully this might be better. :)

public static Guid EmbedInteger(int id)
{
    byte[] bytes = new byte[8];
    int i = 0;

    while (id > 0)
    {
        int remainder = id%100;
        bytes[i++] = (byte)(16*(remainder/10) + remainder%10);
        id /= 100;
    }

    return new Guid(0, 0, 0x4001, bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
}

Adam Houldsworth: Update: This code can also be unrolled:

int remainder = id % 100;
bytes[0] = (byte)(16 * (remainder / 10) + remainder % 10);
id /= 100;
if (id == 0) return;
remainder = id % 100;
bytes[1] = (byte)(16 * (remainder / 10) + remainder % 10);
id /= 100;
if (id == 0) return;
remainder = id % 100;
bytes[2] = (byte)(16 * (remainder / 10) + remainder % 10);
id /= 100;
if (id == 0) return;
remainder = id % 100;
bytes[3] = (byte)(16 * (remainder / 10) + remainder % 10);
id /= 100;
if (id == 0) return;
remainder = id % 100;
bytes[4] = (byte)(16 * (remainder / 10) + remainder % 10);
like image 80
Matthew Watson Avatar answered Nov 08 '22 16:11

Matthew Watson