Can result of BobJenkinsHash function be negative? - string

Environment: Win7 64bit, Delphi 2010, Win32 project.
I try to get integer hash values for set of strings with the help of BobJenkinsHash() function from Generics.Defaults.
It works but some points are not clear for me.
Can result of the function be negative?
As I see
on source site it is used uint32_t as result type of the hashword() function:
uint32_t hashword(
const uint32_t *k, /* the key, an array of uint32_t values */
size_t length, /* the length of the key, in uint32_ts */
uint32_t initval) /* the previous hash, or an arbitrary value */
{
Is it unsigned int?
Second question is I have different results for different strings with identical values:
'DEFPROD001' => 759009858
'DEFPROD001' => 1185633302
Is it normal behaviour?
My full function to calculate hash (if first argument is empty then second is returned):
function TAmWriterJD.ComposeID(const defaultID: string; const GUID: String): String;
var
bjh: Integer;
begin
if defaultID = '' then
begin
Result := GUID
end
else
begin
bjh := BobJenkinsHash(defaultID, Length(defaultID) * SizeOf(defaultID), 0);
Result := IntToStr(bjh);
end;
end;

The Delphi implementation is declared like so:
function BobJenkinsHash(const Data; Len, InitData: Integer): Integer;
It returns a signed 32 bit integer. So yes, this implementation can return negative values.
The C implementation you refer to returns an unsigned 32 bit integer. So that cannot return negative values.
Assuming both implementations are correct then they will, given the same input, return the same 32 bits of output. It's just that when interpreted as signed or unsigned values these bits have different meaning.
As to your second question, passing the same string to the hash function will yield the same hash. You must have made a mistake in your test case.
BobJenkinsHash(defaultID, Length(defaultID) * SizeOf(defaultID), 0);
Here defaultID is a string variable and that is implemented as a pointer. You are therefore hashing the address. And not even doing that correctly due to your incorrect length argument. Instead you need to write:
BobJenkinsHash(Pointer(defaultID)^, Length(defaultID) * SizeOf(Char), 0);
This program demonstrates:
{$APPTYPE CONSOLE}
uses
System.Generics.Defaults;
var
s, t: string;
begin
s := 'DEFPROD001';
t := 'DEFPROD001';
Writeln(BobJenkinsHash(s, Length(s) * SizeOf(s), 0));
Writeln(BobJenkinsHash(t, Length(t) * SizeOf(t), 0));
Writeln(BobJenkinsHash(Pointer(s)^, Length(s) * SizeOf(Char), 0));
Writeln(BobJenkinsHash(Pointer(t)^, Length(t) * SizeOf(Char), 0));
Readln;
end.
Output:
2129045826
-331457644
-161666357
-161666357

Related

Pascal Script fails to retrieve wide string from a custom DLL

I need Inno Setup to load a wide string from a function in a custom DLL. I have no problem loading Ansi strings – this, for example, works fine:
int __stdcall GetStringA(char *lpText)
{
StringCchCopyA(lpText, 30, "New Ansi value");
return strlen(lpText);
}
Pascal Script
function GetStringA(lpText: AnsiString): Integer;
external 'GetStringA#files:MyDll.dll';
function LoadStringA(): AnsiString;
var
Str: AnsiString;
Length: Integer;
begin
Str := 'Initial Ansi value';
SetLength(Str, 30);
Length := GetStringA(Str);
SetLength(Str, Length);
Result := Str; // Str == 'New Ansi value'
end;
Doing the same thing with wide strings fails, however:
int __stdcall GetStringW(wchar_t *lpText)
{
StringCchCopyW(lpText, 30, L"New Wide value");
return wcslen(lpText);
}
Pascal Script
function GetStringW(lpText: WideString): Integer;
external 'GetStringW#files:MyDll.dll';
function LoadStringW(): WideString;
var
Str: WideString;
Length: Integer;
begin
Str := 'Initial Wide value';
SetLength(Str, 30);
Length := GetStringW(Str); // ** DOES NOT ALTER 'Str' **
SetLength(Str, Length);
Result := Str; // Str == 'Initial Wide v' (old string, new length)
end;
Sending a WideString to the DLL works fine. Changing the buffer contents in the DLL also works fine…but somehow that does not percolate back to my Pascal Script variable.
I suspect that some sort of marshaling goes on under the hood, so that the DLL actually manipulates a copy of my variable. Any insights or workarounds appreciated!
I'm using Inno Setup 6.2.0.
I made it work by replacing WideString with String:
function GetStringW(lpText: String): Integer;
external 'GetStringW#files:MyDll.dll';
function LoadStringW(): WideString;
var
Str: String;
Length: Integer;
begin
Str := 'Initial Wide value';
SetLength(Str, 30);
Length := GetStringW(Str); // ** ALTERS 'Str' **
SetLength(Str, Length);
Result := Str; // Str == 'New Wide value'
end;
I'd have thought WideString and String were equivalent, but apparently not quite. It is necessary to change both the function prototype and the Str declaration. It is not necessary to change the LoadStringW return type, though you may – and the returned value can in either case be assigned to a variable of type WideString or String without problem.
So: Problem solved. I'm still curious about the underlying behavior, though.

'' Is Not a Valid Identifier (Delphi XE5)

I'm new to Delphi XE5 (part of Embarcadero's RAD Studio), and using the StrToFloat function in a VCL Forms project designed to build a simple calculator.
The code to display the text to the user works well, but I'm having trouble extracting it in a way that will allow the calculator to actually perform calculations. The means I've settled on is displaying the code to the user in a Tedit, and storing the code to be executed in a Tmemo. Pressing the buttons on the calculator stores the numbers to both, and pressing an operator (the symbols for add, subtract, exponent, multiply, and divide) starts a new line in the memo. This way, each number is on its own line, and each operator is on its own line.
I then want to extract the numbers with respect to the operators, by iterating through the lines of the memo. For 3 ^ 4, you would see...
3
^
4
...in the TMemo.
I want to find the ^ sign at line index 1 (lines start at index 0, right?), and store 3 in a base variable by the logic that, if ^ is lines[1], then 3 is lines[1-1], or lines[0], and store 4 in an exponent variable. Both variables would be of type extended. This is the code I've created to do this...
procedure TForm1.btnEqualsClick(Sender: TObject);
var
i: Integer;
base: extended;
exponent: extended;
result: extended;
begin
{This For loop looks for the exponentiation operator (^) in memCurrentEntry. If
it's found, it then solves the equation.}
for i := Low(memCurrentEntry.Lines.Text.Length) to High(memCurrentEntry.Lines.Text.Length) do
if AnsiContainsStr(memCurrentEntry.Lines.Text, '^') then
begin
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
exponent := StrToFloat(memCurrentEntry.Lines[ansiPos('^', memCurrentEntry.Lines.Text)+2]);
result := power(base, exponent);
end;
When I run the calculator, the numbers appear correctly, but when I hit the equals button to run the code above, I get the message Project Calculator_u.exe raised exception class EConvertError with Message ''' is not a valid floating point value'.
I know the StrToFloat function is trying to convert a null character or something (How do I identify what code is generating " '' is not a valid floating point value" error dialogue box), but what should I do to correct this?
Below is the code for each button -- the digit (0, 1, 2, 3, etc.) changes, but the rest is the same for each one...
procedure TForm1.btn0Click(Sender: TObject);
begin
//Adds the digit 0 to the end of the current number in memCurrentEntry
memCurrentEntry.SelStart := Length(memCurrentEntry.Text);
memCurrentEntry.Lines.Text := Copy(memCurrentEntry.Lines.Text,1,
memCurrentEntry.SelStart)+'0'+ Copy(memCurrentEntry.Lines.Text,
memCurrentEntry.SelStart+1,Length(memCurrentEntry.Lines.Text)
- memCurrentEntry.SelStart);
//Adds the digit 0 to the end of the current number in edtCurrentEntry
edtcurrententry.Text := edtcurrententry.Text + '0';
end;
I can post the full unit file upon request.
Use TryStrToFloat. This returns a boolean indicating whether or not the conversion succeeded.
var
str: string;
val: Double;
....
if TryStrToFloat(str, val) then
// val contains the floating point value represented by str
else
// str does not contain a floating point value, do something else with it
Of course, you could use StrToFloat, and catch the exception. But since you expect to encounter the exception, it will result in much cleaner code if you use TryStrToFloat.
the usage of lines on the memo is wrong:
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
the lines.text returns the ENTIRE text, which is not what you want. Instead use some LOCAL variables to hold your lookup values...that way you can set a breakpoint and see whats wrong and use the following method to locate the "^"
result := 0;
carrotLine := memCurrentEntry.Lines.IndexOf('^');
if carrotline > 0 then // must be 1 or greater, so a number can be at the 0 position
begin
base := TryStrToFloat(MemCurrentEntry.Lines[carrotLine-1],0);
exponent := 0;
// only try to read the next value if there is one.
if memCurrentEntry.Lines.Count >= carrotLine then
exponent := TryStrToFloat(MemCurrentEntry.Lines[carrotline+1],0);
result := power(base,exponent);
end;
Note that this code ONLY handles a single ^ in the memo (your code had the same issue), to go beyond this is to use a loop and possibly a state engine or some other parsing logic to handle computations.
Use StrToFloat is not wrong. If the conversion is failed, user should see an error message. If you want to suppress the error quietly, then you should add a try/except to protect your code.
try
Value := StrToFloat(A_String);
except
on EConvertError do
Value := 0; // Fallback to a default value or execute other code logic
end;
According to your given code example, if the base and the exponent are entered in your given format (first line is the base, second line is a "^" and the third line is the exponent). I'd suggest you write your code as follows:
var
Numbers: TArray<string>;
// Other variables go here
begin
// ...
Numbers := memCurrentEntry.Lines.Text.Split('^');
if Length(Numbers) = 2 then
try
Base := StrToFloat(Numbers[0]);
Exponent := StrToFloat(Numbers[1]);
Result := Power(Base, Exponent);
except
on EConvertError do
begin
ShowMessage(Format('Unable to calculate the formula %s', [memCurrentEntry.Lines.Text]));
Result := 0; // Fallback to a default value, such as 0
end;
end;
// ...
end;

How do I create a random string in Free Pascal?

I want to write a function GenString that returns a random string of length [0 .. 100] based on a parameterized function GenChar that users could customize.
That is, if one user wants to generate a string of all as, he could do that by passing in a function that always returns a, and another user could generate random alphabetic Hebrew words by passing in a custom GenChar function that returns random Hebrew letters.
Lemma 1: I want to write a function GenArray that generates an array of whatever type a passed-in function GenThing : sometype returns. Could this be done in Free Pascal.
Lemma 2: What I'm trying to do is port QuickCheck to Free Pascal.
Here's an example. Unicode string is used since international characters is mentioned.
//The functions
type
TGenChar = function: widechar;
function GenString(MaxLength: integer; GenChar: TGenChar): widestring;
var
i: integer;
begin
if MaxLength < 1 then
MaxLength:= 1; //minimum length is 1
setlength(result, random(MaxLength+1));
for i:= 1 to length(result) do
result[i]:= GenChar;
end;
//The usage example (remember to call randomize somewhere in your program)
var
SampleCharSet: widestring= 'ABCxyz';
function SampleGenChar: widechar;
begin
result:= SampleCharSet[random(length(SampleCharSet))+1];
end;
function GetRandomStringSample: widestring;
begin
result:= GenString(100, SampleGenChar);
end;

How to calculate actual memory used by string variable?

Strings in Delphi locating in dynamic memory.
How to calculate actual memory (in bytes) used by string variable?
I know the string must store some additional information, at least reference count and length, but how many bytes it uses except characters?
var
S: string;
Delphi 2010, XE, XE2 used
The layout on 32 bit UNICODE DELPHI taken from official Embarcadero documentation is like this:
Note that there's an additional longint field in the 64 bit version for 16 byte alignment. The StrRec record in 'system.pas' looks like this:
StrRec = packed record
{$IF defined(CPUX64)}
_Padding: LongInt; // Make 16 byte align for payload..
{$IFEND}
codePage: Word;
elemSize: Word;
refCnt: Longint;
length: Longint;
end;
The payload is always 2*(Length+1) in size. The overhead is 12 or 16 bytes, for 32 or 64 bit targets. Note that the actual memory block may be larger than needed as determined by the memory manager.
Finally, there has been much mis-information in this question. On 64 bit targets, strings are still indexed by 32 bit signed integers.
For String specifically, you can use SysUtils.ByteLength() to get the byte length of the character data, and if not zero then increment the result by SizeOf(System.StrRec) (which is the header in front of the character data) and SizeOf(Char) (for the null-terminator that is not included in the length), eg:
var
S: string;
len: Integer;
begin
S := ...;
len := ByteLength(s);
if len > 0 then Inc(len, SizeOf(StrRec) + SizeOf(Char));
end;
On the other hand, if you want to calculate the byte size of other string types, like AnsiString, AnsiString(N) (such as UTF8String), RawByteString, etc, you need to use System.StringElementSize() instead, eg:
var
S: SomeStringType;
len: Integer;
begin
S := ...;
len := Length(S) * StringElementSize(S);
if len > 0 then Inc(len, SizeOf(StrRec) + StringElementSize(s));
end;
In either case, the reason you only increment the length if the string has characters in it is because empty strings do not take up any memory at all, they are nil pointers.
To answer the question:
How to calculate actual memory (in bytes) used by string variable?
MemSize = Overhead + CharSize * (Length + 1)
CharSize = 1 // for Ansi strings
CharSize = 2 // for Unicode strings
Overhead = 8 // for 32 bit strings
Overhead = 16 // for 64 bit strings

Inno Setup comma error

I have GetVersion function in [Code] that returns a string like this "1004", "1003", etc.
I created this function to check the registry value for lowest version numbers and uninstall them.
Here is a snippet of the code it is giving error point to StrtoInt conversion line stating
Comma (,) expected
Here is the snippet:
function DoesOldVersionsExist(): Boolean;
var
AppVersion: integer;
mstr: string;
VersionInstalled: cardinal;
begin
AppVersion := StrToInt(GetVersion({#MyAppVersion}), 0);
...
after that line I'm simple comparing the values and return true or false. Much Appreciated.
This is what error message says:
Line 55
Column 40.
Comma (,) expected
Thanks Deanna but unfortunately that is the error message pointing to this :
AppVersion := StrToInt(GetVersion({#MyAppVersion}), 0);
^
Here is the GetVersion function:
function GetVersion(AppVersion: String): String;
var
Version: String;
CharIndex: integer;
c: char;
begin
for CharIndex := 1 to Length(AppVersion) do begin
c := AppVersion[CharIndex];
if (c <> '.') then
Version := Version + c;
end;
Result := Version;
end;
I think that you can't just have Inno Setup constants in code like this, you have to use ExpandConstant():
AppVersion := StrToInt(GetVersion(ExpandConstant('{#MyAppVersion}')), 0);
You have not given us enough information to give a definite answer, but I think that the situation is as follows.
You have defined some constant called MyAppVersion which you let the ISPP (the Inno Setup pre-processor) substitute. Now, you have not told us what type this variable is, and you have not told us what the signature of GetVersion is (in particular, what type of argument does it expect?). However, if these types are strings, you need to write
StrToInt(GetVersion('{#MyAppVersion}'), 0);
in order to obtain, say,
StrToInt(GetVersion('Some string, this is!'), 0);
instead of
StrToInt(GetVersion(Some string, this is!), 0);
which is malformed (indeed, to such an extent that it hurts my eyes to look at it).

Resources