Newer
Older
alicedbg / src / adbg / utils / uid.d
/// Mostly internal module that deals with GUIDs and UUIDs.
///
/// Authors: dd86k <dd@dax.moe>
/// Copyright: © dd86k <dd@dax.moe>
/// License: BSD-3-Clause-Clear
module adbg.utils.uid;

import adbg.include.c.stdio;
import adbg.utils.bit;

extern (C):

enum {
	UID_GUID	= 0,	/// Global UID (little-endian, ala Microsoft)
	UID_UUID	= 1,	/// Universal UID (big-endian)
	UID_TEXTLEN	= 36,	/// Text buffer length
}

/// UUID/GUID structure
union UID {
	this (ubyte p0, ubyte p1, ubyte p2, ubyte p3,
		ubyte p4, ubyte p5, ubyte p6, ubyte p7,
		ubyte p8, ubyte p9, ubyte p10, ubyte p11,
		ubyte p12, ubyte p13, ubyte p14, ubyte p15) {
		data[0] = p0;
		data[1] = p1;
		data[2] = p2;
		data[3] = p3;
		data[4] = p4;
		data[5] = p5;
		data[6] = p6;
		data[7] = p7;
		data[8] = p8;
		data[9] = p9;
		data[10] = p10;
		data[11] = p11;
		data[12] = p12;
		data[13] = p13;
		data[14] = p14;
		data[15] = p15;
	}
	ubyte[16] data;
	ushort[8] u16;
	uint[4]   u32;
	ulong[2]  u64; // Preferred to use when size width = 64
	struct {
		uint     time_low;
		ushort   time_mid;
		ushort   time_ver;	// and time_hi
		ushort   clock;	// seq_hi and res_clock_low
		ubyte[6] node;
	}
}

/// Format the UID into a text buffer.
///
/// If the target endianness does not match the compile target, this function
/// will automatically performs a swap. This function does not terminate the string.
/// Params:
/// 	uid = UID structure.
/// 	buffer = Text buffer of UID_TEXTLEN in size.
/// 	target = UID_GUID or UID_UUID.
/// Returns: Whatever snprintf returns
int uid_text(ref UID uid, ref char[UID_TEXTLEN] buffer, int target) {
	return uid_string(uid, buffer.ptr, UID_TEXTLEN, target);
}

/// Format the UID into a text buffer.
///
/// If the target endianness does not match the compile target, this function
/// will automatically performs a swap. This function does not terminate the string.
/// Params:
/// 	uid = UID structure.
/// 	buf = Text buffer.
/// 	buflen = Text buffer length.
/// 	target = UID_GUID or UID_UUID.
/// Returns: Whatever snprintf returns
int uid_string(ref UID uid, char *buf, size_t buflen, int target) {
	version (LittleEndian) {
		if (target == UID_UUID) uid_swap(uid);
	} else {
		if (target == UID_GUID) uid_swap(uid);
	}
	return snprintf(buf, buflen,
		"%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",
		uid.time_low, uid.time_mid, uid.time_ver, uid.clock,
		uid.data[10], uid.data[11], uid.data[12],
		uid.data[13], uid.data[14], uid.data[15]);
}

/// Swap endianness of a UID. GUID (LSB) becomes UUID (MSB) and vice-versa.
/// Params: uid = UID structure
void uid_swap(ref UID uid) {
	uid.time_low = adbg_bswap32(uid.time_low);
	uid.time_mid = adbg_bswap16(uid.time_mid);
	uid.time_ver = adbg_bswap16(uid.time_ver);
	uid.clock    = adbg_bswap16(uid.clock);
}
@system unittest {
	UID uid;
	uid.time_low = 0x01_000000;
	uid.time_mid = 0x02_00;
	uid.time_ver = 0x03_00;
	uid.clock    = 0x04_00;
	uid_swap(uid);
	assert(uid.time_low == 1);
	assert(uid.time_mid == 2);
	assert(uid.time_ver == 3);
	assert(uid.clock    == 4);
}

/// Return zero if UID is NIL.
/// Params: uid = UID structure
/// Returns: Non-zero if non-empty.
bool uid_nil(ref UID uid) {
	return uid.u64[0] == 0 && uid.u64[1] == 0;
}
@system unittest {
	UID uid;
	assert(uid_nil(uid));
}