Observation about Base64 Guid encoding

While implementing Base64 GUID encoding i noticed that the way UE 5.3 encodes it’s Guids differed from how C# and JavaScript do. This resulted in different values between our various solutions. When digging into this it was related to Big vs Little endian encoding and the FGuid data structure itself.

However, while working to make Unreal’s output match the other systems I ultimately had to handle endianness in an inconsistent manner. To get a matching ByteArray i had to change the FGuid.A component’s handling to INTEL_ORDER32(), while keeping the FGuid.C & FGuid.D component orders as NETWORK_ORDER32().

This seems weird, shouldn’t i only use either INTEL_ORDER or NETWORK_ORDER for each component and not only byte swap one of them?

The below is what i ended up implementing to get Guid parity with the other systems, but it doesn’t feel right to me. I get (now) needing to manage endianess, but why would Guid.A be the only component that has an opposite endianness?

/* Manually processing the UE byte array to match that of C# */
uint8 bytes[16] = {
	//A's order remains unchanged
	uint8(guid.A),
	uint8(guid.A >> 8),
	uint8(guid.A >> 16),
	uint8(guid.A >> 24),
	
	//B gets split into two uint16's which are swapped with each other
	//but their individual orders remain the same
	uint8(guid.B >> 16),
	uint8(guid.B >> 24),
	
	uint8(guid.B),
	uint8(guid.B >> 8),
	
	//C's order gets reversed
	uint8(guid.C >> 24),
	uint8(guid.C >> 16),
	uint8(guid.C >> 8),
	uint8(guid.C),

	//D's order gets reversed
	uint8(guid.D >> 24),
	uint8(guid.D >> 16),
	uint8(guid.D >> 8),
	uint8(guid.D)
};


/* Implemented as routine with endian sensitivity */

uint8 bytes[16];
//Copy FGuid's bytes to array
memcpy(bytes, &guid.A, 4);
memcpy(bytes + 4, &guid.B, 4);
memcpy(bytes + 8, &guid.C, 4);
memcpy(bytes + 12, &guid.D, 4);

//Pointers to sub-components
uint32* ptrA = reinterpret_cast<uint32*>(bytes);

uint32* ptrB = reinterpret_cast<uint32*>(bytes+4);
uint16* ptrB1 = reinterpret_cast<uint16*>(bytes+4);
uint16* ptrB2 = reinterpret_cast<uint16*>(bytes+6);

uint32* ptrC = reinterpret_cast<uint32*>(bytes+8);
uint32* ptrD = reinterpret_cast<uint32*>(bytes+12);

//Keep In same order as guid.A
*ptrA = INTEL_ORDER32(*ptrA);

//Swap each half of guid.B then reverse guid.B's order
*ptrB1 = NETWORK_ORDER16(*ptrB1);
*ptrB2 = NETWORK_ORDER16(*ptrB2);
Swap(ptrB1, ptrB2);
*ptrB = NETWORK_ORDER32(*ptrB);

//Reverse Guid.C's order and guid.D's order
*ptrC = NETWORK_ORDER32(*ptrC);
*ptrD = NETWORK_ORDER32(*ptrD);

Kudos to Remy Lebeau at stack overflow for pointing me in the right direction.