Reputation: 76713
If SizeOf(datastructure)
is not a multiple of 4. It can be awkward to access the remaining few bytes.
I want to optimize this by reading the remaining 1,2 or 3 bytes in a 4 byte variable and then masking off the bytes I don't need.
This should not give exceptions, because blocks should be allocated in dword (or bigger) aligned chunks.
Let me give an example:
function MurmurHash3(data: PInteger; size: integer; seed: Cardinal): Cardinal;
const
....
{$ifdef purepascal}
var
i: integer;
k,hash: cardinal;
remaining: cardinal;
begin
hash:= seed;
for i:= 0 to (size shr 2)-1 do begin
... do murmur stuff
end; {for i}
remaining:= data[size shr 2]; //access the remaining 1,2,3 bytes
//mask off the bytes we don't need.
remaining:= remaining and ($ffffffff shr ((size and $3)*8));
....
Is this safe or will this land me in trouble?
Upvotes: 1
Views: 126
Reputation: 613332
This is safe if and only if it is valid to read beyond the end of the data to a multiple of 4. If the byte array is held in an integer array then you are fine. If it is actually held in a byte array then you could be reading off the end of the buffer.
What I mean by this is that although your function accepts PInteger
, that might have been done for sake of convenience when addressing the array. If the caller of the function has cast PByte
to PInteger
at the call site, then it is at least plausible that you could be reading off the end of the array and potentially encounter a runtime memory fault.
You mention that a desire to optimise is driving this. I'm not sure that the final step of a hash calculation will need optimising. The loop is where you incur the cost. I doubt you'd suffer very much by using Move
to perform a copy of the straggling bytes into an integer variable. If you really want to optimise, break 3 into 2 then 1, and then 2 and 1 can be handled by register access.
Upvotes: 4