Convert some ints into a byte array without allocations?

Samson Smith via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sat Jan 16 10:05:46 PST 2016


On Saturday, 16 January 2016 at 16:28:21 UTC, Jonathan M Davis 
wrote:
> On Saturday, January 16, 2016 14:34:54 Samson Smith via 
> Digitalmars-d-learn wrote:
>> I'm trying to make a fast little function that'll give me a 
>> random looking (but deterministic) value from an x,y position 
>> on a grid. I'm just going to run each co-ord that I need 
>> through an FNV-1a hash function as an array of bytes since 
>> that seems like a fast and easy way to go. I'm going to need 
>> to do this a lot and quickly for a real time application so I 
>> don't want to waste a lot of cycles converting data or 
>> allocating space for an array.
>>
>> In a nutshell how do I cast an int into a byte array?
>>
>> I tried this:
>>
>> byte[] bytes = cast(byte[])x;
>> > Error: cannot cast expression x of type int to byte[]
>>
>> What should I be doing instead?
>
> For this particular case, since you're hashing rather than 
> doing something like putting the resulting value on the wire, 
> the cast that others suggested may very well be the way to go, 
> but the typesafe way to do the conversion would be to use 
> std.bitmanip.
>
>     int i = 12345;
>     auto arr = nativeToBigEndian(i);
>
> where the result is ubyte[4], because the argument was an int. 
> If it had been a long, it would have been ubyte[8]. So, you 
> avoid bugs where you get the sizes wrong. The only reason that 
> I can think of to _not_ do this in your case would be speed, 
> simply because you don't care about swapping the endianness 
> like you would when sending the data via a socket or whatnot. 
> Of course, if you knew that you were always going to be on 
> little endian machines, you could also use nativeToLittleEndian 
> to avoid the swap, though that still might be slower than a 
> simple cast depending on the optimizer (it uses a union 
> internally).
>
> But it will be less error-prone to use those functions, and if 
> you _do_ actually need to swap endianness, then they're exactly 
> what you should be using. We've had cases that have come up 
> where using those functions prevented bugs precisely because 
> the person writing the code got the sizes wrong (and the 
> compiler complained, since nativeToBigEndian and friends deal 
> with the sizes in a typesafe manner).
>
> - Jonathan M Davis

If I'm hoping to have my hash come out the same on both bigendian 
and littleendian machines but not send the results between 
machines, should I take these precautions? I want one machine to 
send the other a seed (in an endian safe way) and have both 
machines generate the same hashes.

Here's the relevant code:

uint coordHash(int x, int y, uint seed){
	seed = FNV1a((cast(ubyte*) &x)[0 .. x.sizeof], seed);
	return FNV1a((cast(ubyte*) &y)[0 .. y.sizeof], seed);
}
// Byte order matters for the below function
uint FNV1a(ubyte[] bytes, uint code){
	for(int iii = 0; iii < bytes.length; ++iii){
         	code ^= bytes[iii];
         	code *= FNV_PRIME_32;
	}
	return code;
}

Am I going to get the same outcome on all machines or would a 
byte array be divided up in reverse order to what I'd expect on 
some machines? If it is... I don't mind writing separate versions 
depending on endianness with 
version(BigEndian)/version(LittleEndian) to get around a runtime 
check... I'm just unsure of how endianness factors into the order 
of an array...


More information about the Digitalmars-d-learn mailing list