Можем ли мы декодировать Guid, чтобы узнать, где и когда он был сгенерирован?

В этой статье объясняется, как создаются Гиды.

Мой вопрос в том, что есть ли способ узнать, какая машина в моей ферме создала этот Guid и когда?

Ответ 1

Нейл Фенвик прав. Однако мы можем использовать эту структуру в наших интересах.

Версия 4 (.Net)

Версии 4 UUID используют схему, основанную только на случайных числах. Этот алгоритм устанавливает номер версии, а также два зарезервированных бита. Все остальные биты устанавливаются с использованием случайного или псевдослучайного источника данных. Версии 4 UUID имеют форму xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, где x - любая шестнадцатеричная цифра, а y - одна из 8, 9, A или B. например. f47ac10b-58cc-4372-a567-0e02b2c3d479.

Использовать поле версии

Мы можем изменить первый кусок байта 8; поэтому, если у вас есть менее 17 машин, вы можете идентифицировать их, изменив идентификаторы GUID, созданные на каждом из них.

static Guid NewSystemGuid(int machine)
{
    if (machine < 0 | machine > 0xF)
        throw new ArgumentOutOfRangeException("machine");
    var g = Guid.NewGuid();
    var arr = g.ToByteArray();
    arr[7] = (byte)((machine << 4) | (arr[7] & 0xF));
    return new Guid(arr);
}

static int ExtractMachine(Guid guid)
{
    var arr = guid.ToByteArray();
    return (arr[7] >> 4) & 0xF;
}

Используйте поле Version и 'y'

Я не уверен, что изменение Y изменит уникальность GUID, поэтому ваш пробег может отличаться. Если у вас менее 17 машин, придерживайтесь первого решения.

static Guid NewSystemGuid(int machine)
{
    if (machine < 0 | machine > 0xFF)
        throw new ArgumentOutOfRangeException("machine");

    var m1 = machine & 0xF;
    var m2 = (machine >> 4) & 0xF;

    var g = Guid.NewGuid();
    var arr = g.ToByteArray();
    arr[7] = (byte)((m1 << 4) | (arr[7] & 0xF));
    arr[8] = (byte)((m2 << 4) | (arr[8] & 0xF));
    return new Guid(arr);
}

static int ExtractMachine(Guid guid)
{
    var arr = guid.ToByteArray();
    return 
        ((arr[7] >> 4) & 0xF) |
        (((arr[8] >> 4) & 0xF) << 4);
}

Используйте версию и 'y' (Redux)

Вы все равно можете сохранить значение в 'y', ограничив количество машин до 63 (используя последние 2 бита для представления 4 возможных значений "y" ):

static Guid NewSystemGuid(int machine)
{
    if (machine < 0 | machine > 0x3F)
        throw new ArgumentOutOfRangeException("machine");

    var m1 = machine & 0xF;
    var m2 = (machine >> 4) & 0xF;

    var g = Guid.NewGuid();
    var arr = g.ToByteArray();
    arr[7] = (byte)((m1 << 4) | (arr[7] & 0xF));

    var y = (arr[8] >> 4) & 0xF;
    switch (y)
    {
        case 0x8:
            arr[8] = (byte)((m2 << 4) | (arr[8] & 0xF));
            break;
        case 0x9:
            arr[8] = (byte)(((m2 | 0x8) << 4) | (arr[8] & 0xF));
            break;
        case 0xA:
            arr[8] = (byte)(((m2 | 0x4) << 4) | (arr[8] & 0xF));
            break;
        case 0xB:
            arr[8] = (byte)(((m2 | 0xC) << 4) | (arr[8] & 0xF));
            break;
        default:
            throw new Exception();
    }
    return new Guid(arr);
}

static int ExtractMachine(Guid guid)
{
    var arr = guid.ToByteArray();
    return 
        ((arr[7] >> 4) & 0xF) |
        (((arr[8] >> 4) & 0x3) << 4);
}

Использовать GUID версии 1

Вы также можете использовать GUID версии 1, так как все еще можно их сгенерировать:

class SequentialGuid
{
    [DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(out Guid guid); 

    public static Guid NewGuid()
    {
        Guid guid;
        UuidCreateSequential(out guid);
        return guid;
    }

    public static byte[] ExtractMacAddress(Guid guid)
    {
        var arr = guid.ToByteArray();
        // Require version 1.
        if (((arr[7] >> 4) & 0xF) != 1)
            throw new ArgumentOutOfRangeException("guid", "GUID is required to be a sequential (version 1) GUID.");

        var macLong = BitConverter.ToInt64(arr, arr.Length - 8);
        macLong = IPAddress.NetworkToHostOrder(macLong);
        arr = BitConverter.GetBytes(macLong);
        Array.Resize(ref arr, 6);

        return arr;
    }
}