Under FPC Lazarus, I am using the following function to compute the hash of a string, this is used to save the object in a dictionary:
function MyClass.HashCode(): UInt32;
var
keyStr: string;
begin
keyStr := KeyToString();
Result := BobJenkinsHash(keyStr[1], Length(keyStr) * SizeOf(Char), 0);
end;
This function sometimes throws an error:
Project < Name > raised exception class ' RunError(201)' with message:
Range check errorin file < source > at line < Line >:
Result := BobJenkinsHash(keyStr[1], Length(keyStr) * SizeOf(Char), 0);
This happens coherently after some usages. I am not sure if some specific strings or number of usages. For example: "Button_logo" seems to raise this error.
I could not find (unfortunately) relevant documentation about this: I found no documentation for FPC and Delphi documentation is (as most of the time) irrelevant.
I don't know if this applies to FreePascal, but in Delphi the BobJenkinsHash()
function (which is deprecated1, BTW) returns a signed Integer
:
function BobJenkinsHash(const Data; Len, InitData: Integer): Integer;
^^^^^^^
You are assigning that return value to an unsigned UInt32
. If the function returns a negative value (see Can result of BobJenkinsHash function be negative?), you will get a runtime error if you have Range Checking enabled.
1 this same issue exists for the function's successor in Delphi, THashBobJenkins.GetHashValue()
, too.
So, you can either:
turn off Range Checking. You can use the {$RANGECKECKS ON|OFF}
/{$R±}
compiler directive (Delphi) to disable Range Checking around the assignment of your UInt32
, eg:
function MyClass.HashCode(): UInt32;
var
keyStr: string;
begin
keyStr := KeyToString();
{$IFOPT R+} {$RANGECHECKS OFF} {$DEFINE RC_WAS_ON} {$ENDIF}
// or: {$IFOPT R+} {$R-} {$DEFINE RC_WAS_ON} {$ENDIF}
// or: {$PUSH} {$R-} (FPC only)
Result := BobJenkinsHash(PChar(keyStr)^, Length(keyStr) * SizeOf(Char), 0);
{$IFDEF RC_WAS_ON} {$RANGECHECKS ON} {$UNDEF RC_WAS_ON} {$ENDIF}
// or: {$IFDEF RC_WAS_ON} {$R+} {$UNDEF RC_WAS_ON} {$ENDIF}
// or: {$POP} (FPC only)
end;
type-cast the return value of BobJenkinsHash()
before assigning it to your UInt32
:
function MyClass.HashCode(): UInt32;
var
keyStr: string;
begin
keyStr := KeyToString();
Result := UInt32(BobJenkinsHash(PChar(keyStr)^, Length(keyStr) * SizeOf(Char), 0));
end;
change the return type of your HashCode()
method to Integer
:
function MyClass.HashCode(): Integer;
var
keyStr: string;
begin
keyStr := KeyToString();
Result := BobJenkinsHash(PChar(keyStr)^, Length(keyStr) * SizeOf(Char), 0);
end;