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...