Arithmetic with IPv6 addresses (large integers) - delphi

I'm working with IPv6 addresses in the form:
FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
Internally, I store them in an array:
TIp6Bytes = array [0..15] of Byte;
I need to manipulate the IPv6 addresses in a number of ways including adding, dividing, multiplying etc. Can anyone suggest a good way to do this?
I guess I should have mentioned that I'm working with Delphi 2009

Jes Klinke wrote a bignum unit for Pascal here.
Disclaimer : I have not used this library personally.

After trying many of the suggestions I could not find a library that fulfilled all my needs and were bug free. I searched a little harder and found a relatively new library by Alex Ciobanu which does BigIntegers (and Big Cardinals) seamlessly allowing you to manipulate them in much the same way as you manipulate normal Integers, Cardinals etc.
As well as BigIntegers, the library also provides a number of very useful features. From the readme:
A set of generic collections classes
(List, Dictionary, HashSet, etc).
Date/Time functionality all combined
in a few structures (somehow
equivalent to .NET's DateTime
structure)
Type Support concept that defines a
set of default "support classes" for
each built-in Delphi types (used as
defaults in collections). Custom
"type support" classes can be
registered for your custom data
types.
BigCardinal and BigInteger data types.
Smart pointers in Delphi
The library is being actively developed. In fact, the author fixed a small bug I found within a day.
You can read more about the library on Alex's blog and download DeHL from Google code.

I once wrote a IPv4 and IPv6 conversion unit including a custom variant type for both types of IP addresses.
For instance, with these Variant types, the following example arithmetics and conversions are possible:
procedure TForm1.Button1Click(Sender: TObject);
var
I4: TIPv4;
I6: TIPv6;
V1, V2, V3, V4: Variant;
begin
I4 := StrToIPv4('192.0.2.128');
I6 := IPv4ToIPv6(I4);
V1 := VarIPv6Create('2001:db8:85a3:0:0:8a2e:0370:7334');
V2 := IPv6ToVar(I6);
V3 := V1 - V2;
V4 := V1 or V2;
if V3 < V4 then
Log(V3 + ' is smaller than ' + V4);
if V2.Equals('::ffff:192.0.2.128') or V2.IsZero then
Log('OK');
Log('V1 = ' + V1.AsStringOutwritten);
Log('V2 = ' + V2.AsURL);
Log('V3 = ' + V3.AsStringCompressed);
V4.Follow;
end;
procedure TForm1.Log(const S: String);
begin
Memo.Lines.Add(S);
end;
Custom variant types really are quite powerfull.

I would say that if you can add, you can then use it to subtract, multiply and divide using addition. Should I assume overflows will be simply ignored?
I seem to recall a method of adding bit-oriented variables using XOR. I am looking for that answer now.
Hopefully, this will point you in the right direction. If I can find that XOR code, I will post it for you.
Here it is:
Bitwise operation
Exclusive disjunction is often used for bitwise operations. Examples:
1 xor 1 = 0
1 xor 0 = 1
1110 xor 1001 = 0111 (this is equivalent to addition without carry)
And the reference is:
http://www.absoluteastronomy.com/topics/Exclusive_disjunction

Related

Is there any way to get RTTI hints for a real48 and shortstring variable in a structure where FieldType is nil in TRttiField?

I have discovered what I think is an odd oversight (probably intentional) on the part of the Extended RTTI feature in Delphi.
I would like to dump all the fields in an record type that has about 1500 different fields in it. Yes, seriously.
Some of them are of type real48 and some are shortstring, for those two, it appears that FieldType is nil for these types at runtime:
function TRttiField.GetValue(Instance: Pointer): TValue;
var
ft: TRttiType;
begin
ft := FieldType;
if ft = nil then
raise InsufficientRtti; // This fires!
TValue.Make(PByte(Instance) + Offset, ft.Handle, Result);
end;
If I was willing to assume that all nil-fieldtype fields are in fact real48's, I could simply use the offset and (if the field width is 6) grab a real48 value.
However the second complication is that all shortstring (ie string[30]) types are similarly afflicted.
Has anybody got these two Ancient Pascal Types to work with modern Extended RTTI?
Right now I'm using a best-guess approach, and where that fails I am hardcoding rules by name of the field, but if there was some technique I could use that would get me there without having to write a lot of code to extract information from all these old pascal file-of-records that I am modernizing, I would appreciate a better idea.
Unfortunately Real48 does not have any type info.
You can see that when you try compile this:
program Project1;
begin
TypeInfo(Real48);
end.
The same goes for the string[n] syntax. But there you could probably fix it by defining your own string types like:
type
string30 = string[30];
That alone would not include the rtti for the record field so you need to hack/fix the rtti as I showed here: https://stackoverflow.com/a/12687747/587106

How to get a 64-bit random value in Delphi?

How can I create a random 64-bit integer value in Delphi 2006? The built-in integer-based Random() function seems to return only values between 0 and 2^31.
You can use my GpRandomGen. It implements Marsaglia/Zaman/James algorithm, is extremely fast and supposedly very random. Released as a freeware.
Generate two 32 bit randoms and splice them together.
EDIT
Similar to #Andreas's answer I like the following (equivalent) implementation:
function Random64: UInt64;
var
Overlay: packed record
a, b: UInt32;
end absolute Result;
begin
Assert(SizeOf(Overlay)=SizeOf(Result));
Overlay.a := Random32;
Overlay.b := Random32;
end;
To answer my own question I came up with the following code:
function GetRandomInt64() : int64;
begin
Int64Rec(result).Words[0] := Random(High(Word));
Int64Rec(result).Words[1] := Random(High(Word));
Int64Rec(result).Words[2] := Random(High(Word));
Int64Rec(result).Words[3] := Random(High(Word));
end;
Not sure if this is a valid solution or it will always create the same follow-up number X+1 after a given result number X.
You can generate 64 random bits and interpret the result as an integer. (63 bits if you are working with signed integers and want the result to be non-negative.) Equivalently you can take two random integers in the range 0..2^31-1, plus two extra random bits, and concatenate them to get a random 64-bit integer.
EDIT: I was curious about the statistical properties of pseudo-random numbers generated by concatenating pseudo-random components and found that (apparently) this approach might not work well depending on your pseudo-random generator (of course for true random number generation, as from atmospheric noise, concatenating random bits is no problem). For recreational use, the loss of various statistical properties might be acceptable, but for more serious use you might end up needing a custom pseudo-random generator as #gabr suggested. Here is a related question: Best method of generating a number with 256 random bits?
Create a GUID (eg CoCreateGuid) and cast it to Int64.
Simple:
function Random64: UInt64;
begin
PCardinal(#result)^ := Random32;
PCardinal(cardinal(#result) + 4)^ := Random32;
end;
where Random32 is your favourite 32-bit unsigned integer random number function.

Loop through irregular enumeration in Delphi

1) Does anyone know if it is possible to loop through an irregular enumeration in Delphi (XE)?
Looping over a normal enumeration is ok. From Delphi Basics:
var
suit : (Hearts, Clubs, Diamonds, Spades);
begin
// Loop 3 times
For suit := Hearts to Diamonds do
ShowMessage('Suit = '+IntToStr(Ord(suit)));
end;
But, if 'suit' instead is declared as
var
suit : (Hearts=1, Clubs, Diamonds=10, Spades);
it loops 10 times. Not suprising, but I would like to loop 3. The only solution I've found so far is converting an enumeration to a set and use the 'for ... in'-loop like on delphi.about.com.
So, if answer to question 1) is no, then:
2) How to convert from enumeration to set in Delphi?
The context I am using it in is a component array of edit-boxes (TEdit) that has an irregular numbering (edit1, edit5, edit7, edit3, ...). While it is possible to reorder all the edit-boxes, it removes the reason of using enumeration as a flexible way to allow addition of an edit-box in the middle of the enumeration.
I do not have a Delphi compiler at hand right now, but I tink that gabr's approach can be rather significantly improved by doing
type
TSuit = (Hearts = 1, Clubs, Diamonds = 10, Spades);
const
Suits: array[0..3] of TSuit = (Hearts, Clubs, Diamonds, Spades);
Who knows, maybe it doesn't even compile.
type
TSuit = (Hearts=1, Clubs, Diamonds=10, Spades);
var
suit: TSuit;
suitEnum: array [1..4] of TSuit;
//initialization
suitEnum[1] := Hearts;
suitEnum[2] := Clubs;
suitEnum[3] := Diamonds;
suitEnum[4] := Spades;
for suit in suitEnum do
DoSomething(suit);
I always use
var
s: TSuit;
begin
for s := Low(TSuit) to High(TSuit) do
{something};
end;
Loop using Ord(Hearts) to Ord(Spades) ?
A dirty option, useful for small enumerations:
type
TSuit = (Hearts = 1, Clubs, Diamonds = 10, Spades);
var
Suit: TSuit;
begin
for Suit in [Hearts, Clubs, Diamonds] do
WriteLn(Ord(Suit));
Works nice in Delphi 2007. Don't know about older versions. Be aware, using for Suit in [Hearts..Diamonds] do has the same problem as your loop.Btw, I use WriteLn() because I tested this in a console application. :-)
It should be understood (and often isn't) that the moment you put hard ordinal assignments into an enumeration, it ceases for all intents to be a Pascalian enumerated type - it just becomes a "bag of constants", which is not the same thing. This is what C-programmers call enumerations. However, a Pascalian enumerated type is ORDINAL in all criterion: It has discrete consecutive values that respond meaningfully to the base operations ORD, PRED, SUCC. Enumerations in C don't do this, and neither do enums in Pascal once you force the ordinals apart.
THIS is the reason that Delphi's RTTI basically refuses to return type information once this has been done. To all intents the type is essentially a tkUnknown, and has to be treated as a 'bag' of compile-time constants. It is only because it still plays lip service to being an ordinal and has (sometimes shaky) support for use in sets that people are led into believing it should still behave like a proper enumerated type. It's better to just understand it for what it really is: a nod to enumerated values in C. Avoid mixing the coding metaphor!
If you do this, then your solution becomes obvious: you use an enumerated type (a proper one) to index a corresponding array of CONSTANTS. Then you can make the ordinals whatever you want, and the enums retain their full RTTI definitions as a proper enumeration. So: your ENUMERATED TYPE contains proper unchanged ordinal values. You get your funny numbers by indexing the constants array using the enumeration -ergo array [MyEnums] of byte = (1,3,8,22,99,whatever)

How to get the first element in a string?

I'm trying to figure out a way to check a string's first element if it's either a number or not.
if not(myString[0] in [0..9]) then //Do something
The problem is that I get an error "Element 0 inaccessible - use 'Length' or 'SetLength"
Another way came to my head from my C-like exprieince - convert the first element of the string to char and check the char,but there is no difference in the compile errors.
if not(char(myString[0]) in [0..9]) then //Do something
How do I accomplish it?
Strings are 1-based:
if not (myString[1] in ['0'..'9']) then // Do something
Pascal and Delphi indexes string from 1. This is a legacy from time where zero byte contained length, while next 255 (index 1 to 255) contained actual characters.
Joel Spolsky wrote quite good article on string issues:
http://www.joelonsoftware.com/articles/fog0000000319.html
Delphi strings use a 1-based index, so just rewrite to
if not(myString[1] in ['0'..'9']) then //Do something
Also take note of the quotes around the 0..9, otherwise you would be comparing characters to integers.
We should keep in mind some things:
String in Delphi is 0-based for mobile platforms and 1-based for Windows.
String in old versions of Delphi is AnsiString (1-byte per char) and WideString in new versions (2-bytes per char).
Delphi supports set of AnsiChar, but doesn't support set of WideChar.
So if we want to write a code compatible with all versions of Delphi, then it should be something like this:
if (myString[Low(myString)]>='0') and (myString[Low(myString)]<='9') then
// Do something
if not(myString[0] in [0..9]) then //Do something
If you're using Delphi 2009, the TCharacter class in Character.pas has functions like IsDigit to help simplify these kinds of operations.
Once you fix the indexing, of course. :)
With later updates to Delphi mobile code, the bottom string index changed from 0 to 1. When you compile older programmes, they compile and run correctly using 0 starting index. Programmes created with the later IDE produce an error. When you have mixtures, life gets complex!
It would be good to be able to take an older programme and tell the IDE that you want it brought up to date (maybe this would fix other things, like fonts getting scrambled when you answer a phone call!) but it would be good to get things consistent!
The simplest way to check to see if the first character of string is an integer, and then dispatch:
var
iResult : integer;
begin
if TryStrToInt( mySTring[1], iResult) then
begin
// handle number logic here iResult = number
end
else
begin
// handle non number logic here
end;
end;
I use a utility function to test the entire string:
function IsNumeric(const Value: string): Boolean;
var
i: Integer;
begin
Result := True;
for i := 1 to Length(Value) do
if not (Value[i] in ['0'..'9','.','+','-']) then
begin
Result := False;
Break;
end;
end;
The above code is for Delphi versions prior to 2007. In 2007 and 2009, you could change the integer variable i to a character c, and use for c in Value instead.
To test for integers only, remove the '.' from the set of characters to test against.
This is incorrect. ISO strings and older Pascal's also started at one. It is just a general convention, and afaik the s[0] thing is a result of that being vacant, and cheap to code in the UCSD bytecode interpreter. But that last bit is before my time, so only my guessing.
It results from the Pascal ability to have arbitrary upper and lower bounds, which provides for more typesafety accessing arrays.
Really old Pascal strings (till early eighties) strings were even worse than C ones btw. Multiple conventions were in used, but all were based on static arrays (like early C), but they were typically space padded, so you had scan back from the end till the spaces ended.
(removed the legacy tag, since being 1 based is not legacy. Accessing s[0] as length IS legacy, but that is not what the question is about)
Foreach element in strName
if not element in [0-9] then
do something
else
element is a digit
end if
Don't forget the quote between digits number.

Delphi: how to automatically remove unused vars ("Variable 'x' is declared but never used" hint)

Is there any tool (preferably freeware) that can analyze Pascal/Delphi syntax and automatically remove unused vars?
In my case, I'm working with a very large Delphi code base, and the compiler hints report over one thousand cases of "Variable 'x' is declared but never used".
I would take hours to remove them by hand, and I might make errors, but the right tool should be able to do that safely and automatically.
I searched online but didn't find one... does anybody here know of such a tool?
Thanks...
Mark Brarford
I see your point and totally agree that such a tool would be useful when working with legacy code. Unfortunately I don't know of any existing tool (I should add freeware tool here, static analyis tools should of course be able to do it easily, but I don't know of any free static code analysis tool) that is capable doing that.
But I guess you could easily write such a tool in a few minutes. A small GUI with a memo and a button should be enough. Then just copy the compiler hints to the memo and press the button. The tool then parses every line. It can easily check if the line contains the hint you are looking for and each such line has the same structure, so parsing should be relatively easy. It can then extract the file name and the line number, open the file and remove the variable declaration. This can be a bit tricky in case of multiple variable declarations in one line but I think it is doable.
I don't know if that's too much effort for you compared to the task of removing all variable declarations yourself. But I would like to see such a tool, so feel free to write it :)
Hope that helped at least a bit.
Okay, I really can't see any problems here. For the parsing part:
function ParseHint (const HintText : String; out HintInfo : THintInfo) : Boolean;
var
I, J : Integer;
HintName : String;
begin
Result := False;
for I := 1 to Length (HintText) do
begin
if (HintText [I] = '(') then
begin
J := I + 1;
while (HintText [J] <> ')') do Inc (J);
HintInfo.LineNumber := StrToInt (MidStr (HintText, I+1, J-(I+1)));
HintInfo.SourceFile := MidStr (HintText, 12, I-12);
HintName := MidStr (HintText, J+3, 5);
if (HintName <> 'H2164') then Exit (False);
end;
if (HintText [I] = '''') then
begin
J := I + 1;
while (HintText [J] <> '''') do Inc (J);
HintInfo.VarName := MidStr (HintText, I+1, J-(I+1));
Exit (True);
end;
end;
end;
Well, reading the source file should be easy, so the only remaing part is removing the variable from its line of declaration. We can simply search for occurences of HintInfo.VarName in the line and check if the character before and after the occurence are no letters but only ' ', ',' or ':'. If this is the case we can just remove it. This covers all these cases:
var UnusedVar : Integer;
var
UnusedVar,
AnotherVar : Integer;
var
UnusedVar, AnotherVar : Integer;
Tell me if I'm wrong or if I forgot any cases but I think this would work and woulde solve the problem of removing unused variables from delphi source files using the compiler-generated hints.
The solution is simple, but requires those hours to be sure you don't make a mistake. First off, You can use Alt-F8 to step through each report one after the other (and Alt-F7 to step backwards). That makes locating them very easy. The cursor is put on the line for you. Then just press the '/' key twice to comment it out. Don't delete it, comment it. This way if you make a mistake you haven't lost any information. The presence of the variable and its data type is still recorded. You can tidy it up later at some point.
One caveat to all this: Conditional compilation may render some variables unused when built different ways. If this happens, then just uncomment the variable again, and put the condition around the declaration too.
Are you sure the variables shouldn't be used? I know the compiler figures out that they aren't used right now, but is that correct, perhaps many of these should be used, but a developer used x2 instead of x1 for instance, copy and paste?
While you might want to remove all those variables unscrutinized, I wouldn't be so hasty, they might be indications of bugs in your code that you'd like to fix.
Example:
procedure PlotPixelAtCenter(rect: Rectangle)
var
x, y: Integer;
begin
x := (rect.Left + rect.Right) div 2;
x := (rect.Top + rect.Bottom) div 2; // <-- bug here, should be y :=
PlotPixel(x, y);
end;
In this example you'll get an error about an unused variable, but this is a bug lurking. Of course, in this example the bug should be easy to find since the plotting will probably be off, but other similar bugs might be harder to spot.
If there is no such tool and you have some patience, I'm building a Delphi analysis and repair tool. And removal of unused symbols is on the list. It is a low proirity project so I can't give an estimate on when its ready.
Just to explain why this isn't a trivial task:
read the source
create a model that contains enough information for each symbol usage.
mark all unused symbols.
rewrite the source without the unneeded symbols.
Task 1 and 2 are hard (luckily for me those are already done). The Delphi language is quite complex. And you need all language elements to be able to recreate the source.
Task 3 is simple. Just flag all symbols that are unused. But beware of symbols in the interface section of a unit. They are possibly not used but needed later (or by some other project).
Task 4 depends.
Aproach A uses an intermediate format (for example a stringlist), you can then use the model to find the declaration of each unused symbol (bottom up else you possibly change the line numbers). You delete all not needed. And don't forget to delete the var keyword if it's the last var in the list!
Aproach B completely rewrites the source file. In this case, you must preserve all comments which is not really fun to do (but my model needs that too). You just removes the unused symbols from the model and rewrite it.
Always be sure to create a backup, because this can end up in disaster.

Resources