This unit is a bag for simple and generally useful things. As a rule (to not let myself put too much things here) this unit must not depend on the Classes unit (see CastleClassUtils for those). Or any higher-level GUI libs like LCL, VCL, or CLX. The only classes defined and used here are exceptions (the base Exception class comes from SysUtils unit) and primitives lists classes.
Initialization of this unit does some generally-useful things:
Calls Randomize (so that you never forget about it).
Sets DecimalSeparator to '.'.
Delphi and FPC define DecimalSeparator based on local settings (like configured user's country). But this makes things like StrToFloat and FloatToStr less predictable — they may give different results on different systems, which limits their use. E.g. FloatToStr(0.9) may output '0,9' on some system. And if you write '0,9' to a text file, it may not be understood by StrToFloat on some other system.
Initial (probably localized) value of DecimalSeparator is saved in LocaleDecimalSeparator variable.
Sorts items ascending, that is Arr[FirstIndex] <= ... <= Arr[LastIndex].
Parameters
Arr
Pointer to items array in memory.
ArrRecordSize
Size (in bytes) of every item. This is the size of item that will be moved around in the memory.
IsSmallerFunc
Comparison function, should return is "A < B" true.
I'm assuming here that IsSmallerFunc works like mathematical "<": it's not reflexive (IsSmallerFunc(A, A) = False), for A <> B exactly one of IsSmallerFunc(A, B) or IsSmallerFunc(B, A) is true, and it's transitive.
Note that IsSmallerFunc gets only the pointers to the items. These may be pointers to the Arr, or a pointer to internal temporary copy of some array item. So IsSmallerFunc cannot modify the item underneath (it would not work for internal copy, and also would create problems with types that need initialization/finalization since our internal copy is done using low-level memory copying.)
FirstIndex
FirstIndex and LastIndex allow you to sort only given part of the array. We don't touch items outside of this range. Note that you could achieve the effect of FirstIndex > 0 also by increasing the Arr pointer, but FirstIndex is usually more comfortable.
If FirstIndex > LastIndex, we do nothing.
ArrStride
Distance (in bytes) between array items in memory. If you don't provide it, we'll assume ArrRecordSize. ArrStride is useful if your array is interleaved with another array, and you want to keep the other data untouched by sorting.
ArrStride must be > 0, actually it must be > ArrRecordSize for sensible behavior. (Unless ArrRecordSize is 0, then ArrStride may be 0 too.).
function Iff(boolval: boolean; trueval, falseval: string) : string; overload;
function Iff(boolval: boolean; trueval, falseval: Integer) : Integer; overload;
function Iff(boolval: boolean; trueval, falseval: Float) : Float; overload;
function Iff(boolval: boolean; trueval, falseval: Cardinal): Cardinal; overload;
function Iff(boolval: boolean; trueval, falseval: char) : char; overload;
function SFPCVersion: string;
Describe FPC version. In the form 'version.release.patch'.
This is actually a constant (for every run of a program it has always the same value) but I can't declare it as a Pascal constant because it must use "Format" function that is not allowed in constant expressions.
function SCompilerDescription: string;
Short name and version of Pascal compiler used to compile this unit. It is a constant, actually, but I cannot declare it as a constant because it must call SFPCVersion that is not declared as a constant.
function SCastleEngineProgramHelpSuffix(const DisplayApplicationName: string; const Version: string; WrapLines: boolean): string;
Print some common info for programs released on [http://castle-engine.sourceforge.net/]. This is useful only for programs released on this WWW page by Michalis. Resulting string is multiline.
Parameters
DisplayApplicationName
Usually ApplicationName, but you can give here something else if you want.
If true then resulting string will not have lines longer than 80 characters. Suitable for printing program help message on stdout, e.g. in response to --help option.
function ExceptMessage(E: TObject; ExceptAddr: Pointer = nil): string; overload;
Nice exception description. Contains exception ClassName (if not descends from EWithHiddenClassName), exception Message (if descends from Exception), and ExceptAddr (if not Nil, and code is compiled with -dDEBUG), and BonusErrorMesssg.
Show nice exception description on console or (for GUI Windows programs) by a message box.
procedure HaltBool(Value: boolean);
If Value then Halt(0), else Halt(1).
It is the standard convention of command-line programs to exit with code 0 on success and <> 0 on failure. Or (for some programs like `test') exit with code 0 to indicate true result and <> 0 to indicate false result. So you will probably want to pass here some boolean variable indicating "Success" or "TestPassed".
Call Proc, catch all exceptions inside the Proc, and in case of exception make OutputException and Halt(HaltCode). The result is that HaltOnException doesn't raise any exception, never. It always deals with exceptions inside Proc itself.
When symbol DEBUG is defined, then HaltOnException works differently — it just calls Proc (and doesn't catch any exceptions).
This is particularly useful under Delphi/Win32. There main program should never exit with exception. Because such exception (because of Delphi stupidity ?) shows ugly Windows dialog box saying something like "Program exited unexpectedly, contact with author etc. bullshit". There is no way for me to avoid this dialog box, even by my own ExceptProc.
Update value of A to be a minimum of A, B. Works like A := Min(A, B), but is marginally faster, since you don't have to do anything when A is already smaller.
Update value of A to be a maximum of A, B. Works like A := Max(A, B), but is marginally faster, since you don't have to do anything when A is already larger.
procedure RestOf3dCoords(coord: integer; out first, second: integer);
3D coordinates (0, 1 or 2) except the given coordinate. If Coord = 0, then it sets First = 1 and Second = 2. And so on — for each Coord value, we set First and Second to the remaining indexes.
function ChangeIntCycle(value, change, maxValue: integer): integer;
Increase Value by Change, nicely wrapping in [0..MaxValue], accepting also negative Change. The final value is always in the [0..MaxValue] range, even when initial Value was outside.
function Lerp(const a, l, h: Double): Double; overload; inline;
function RoundUpToMultiply(value, multiplicator: Integer): Integer;
Smallest multiple of Multiplicator that is still >= Value.
function BiggestPowerOf2(Value: Cardinal): Cardinal;
Largest power of 2 still <= Value. When Value = 0 returns 0.
function Biggest2Exponent(Value: Cardinal): integer;
Exponent of the largest power of 2 that it's still <= Value. Like BiggestPowerOf2, except that this returns which power of 2 it is, while BiggestPowerOf2 just returns exactly this power. Bigget2Exponent(0) = -1.
function Smallest2Exponent(Value: Cardinal): Integer;
Smallest exponent such that 2ˆthis exponent is >= Value. Smallest2Exponent(0) = -1 (this is different situation than Smallest2Exponent(1), when we return 0.
function Smallest2Power(Value: Cardinal): Cardinal;
Smallest power of 2 that is >= Value. Like Smallest2Exponent, except here we return 2ˆSmallest2Exponent(Value). Returns 0 when Value = 0.
function IsPowerOf2(Value: Cardinal): boolean;
Check is Value an exact power of 2.
function DivRoundUp(Value, Divider: Cardinal): Cardinal; overload;
function DivRoundUp(Value, Divider: Integer): Integer; overload;
function MapRange(sourceVal, sourceBegin, sourceEnd, destBegin, destEnd: integer): float; overload;
Linearly map value from a one range to another.
Consider how SourceVal is placed in the [SourceBegin .. SourceEnd] (it may be outside of this range, it all still works OK). It has some distance to range start (SourceBegin), and some distance to range end (SourceEnd).
We want to find another number, that is identically placed in another range [DestBegin .. DestEnd].
Such that Distance(SourceVal, SourceBegin) / Distance(SourceVal, SourceEnd) = Distance(Result, DestBegin) / Distance(Result, DestEnd).
And such that it's on the same side of the range. So if SourceVal is beyond SourceEnd (that is, SourceBegin < SourceEnd < SourceVal or SourceBegin > SourceEnd > SourceVal) then the result must be similarly beyond DestEnd.
As you can see, this works regardless if one, or both, of the ranges increase (range end is larger than range begin).
function MapRange(sourceVal, sourceBegin, sourceEnd, destBegin, destEnd: float ): float; overload;
function RandomFloatRange(const RangeBegin, RangeEnd: Float): Float;
Random float value in the given range. Make sure RangeBegin < RangeEnd.
function AngleRadPointToPoint(x1, y1, x2, y2: Single): Single;
Angle between a 2D line segments and OX axis. Angle 0 means that Y1 = Y2 and X2 > X1, then the angle increases as you rotate CCW. In radians.
function NatNatPower(Base, Exponent: Cardinal): Cardinal;
Calculate power Base to Exponent, knowing both arguments (and so, also the result) are >= 0.
procedure DivUnsignedMod(Dividend: Integer; Divisor: Word; out Result: Smallint; out Remainder: Word);
Like DivMod (return result of integer division and a remainder), but always return Remainder >= 0.
This is useful in case when Dividend < 0. Standard DivMod (and Pascal div and mod operators) return then Result rounded toward zero, and Remainder may be < 0. This procedure will return Result rounded toward negative infinity, and Remainder will always be >= 0.
function CeilDiv(const A, B: Cardinal): Cardinal;
Returns Ceil(A / B), but calculated faster and more precisely (without floating-point help).
procedure FloatDivMod(const A, B: Double; out DivResult: Int64; out Remainder: Double);
Calculate integer division and modulo on two float arguments. Current implementation assumes that A >= 0 and B > 0 (so Result is always >= 0).
Tries to secure against floating point imprecision. It's always guaranteed that Remainder >= 0 and <= B (and usually should be < B, but this cannot be guaranteed).
function FloatModulo(const A, B: Double): Double;
Calculate float modulo of division on two float arguments. See FloatDivMod for more comments.
function IntSqrt(const Value: Cardinal): Cardinal;
Floor from Sqrt(Value).
function DeleteFileExt(const FileName: string): string;
Remove from the FileName the last extension (including the dot). Note that if the FileName had a couple of extensions (e.g. blah.x3d.gz) this will remove only the last one. Will remove nothing if filename has no extension.
It is not adviced to use this function, better to operate on URLs and MIME types instead of filenames and extensions, see CastleURIUtils.
function ExtractFileDoubleExt(const FileName: string): string;
Extracts two last extensions from the filename, if it has two extensions. If the filename has only one extension, returns that one extension, if the filename has no extension — returns empty string, similar to ExtractFileExt.
This is useful to detect file types from filenames like model.x3d.gz, where ExtractFileExt returns only .gz. This function will return .x3d.gz.
It is not adviced to use this function, better to operate on URLs and MIME types instead of filenames and extensions, see CastleURIUtils.
function ExtractOnlyFilename(const FileName: string): string; deprecated;
Warning: this symbol is deprecated.
Extracts from FileName the name of file, without directory, without last extension and without any Windows drive letter.
Deprecated, since we use URLs everywhere and also because this has very low usage. Use DeleteURIExt(ExtractURIName(URL)) if you really need to.
function ChangeFilePath(const FileName, NewPath: string): string; deprecated;
Warning: this symbol is deprecated.
Returns FileName with directory (path) part replaced with given NewPath. NewPath must contain trailing PathDelim.
Deprecated, since we use URLs everywhere and also because this has very low usage.
function InclPathDelim(const s: string): string;
Include / exclude the last path delimiter, if necessary. They are just comfortable shorter names for IncludeTrailingPathDelimiter and ExcludeTrailingPathDelimiter.
function ExclPathDelim(const s: string): string;
function IsPathAbsolute(const Path: string): boolean;
Check is the given Path absolute.
Path may point to directory or normal file, it doesn't matter. Also it doesn't matter whether Path ends with PathDelim or not.
Note for Windows: while it's obvious that 'c:\autoexec.bat' is an absolute path, and 'autoexec.bat' is not, there's a question whether path like '\autoexec.bat' is absolute? It doesn't specify drive letter, but it does specify full directory hierarchy on some drive. This function treats this as not absolute, on the reasoning that "not all information is contained in Path".
function SpecialDirName(const DirectoryName: string): boolean;
Checks is the directory name special, like "." or "..".
The precise definition of "special" is that you cannot ever create or even have any filenames / directories named like this.
function AppendToFilename(const FileName, Suffix: string): string;
Add Suffix to the filename, right before extension. Returns DeleteFileExt(FileName) + Suffix + ExtractFileExt(FileName).
function PointerAdd(p: pointer; add: integer): pointer;
Pointer arithmetic. Will wrap over if you add too much. Add may be negative. This is just a shortcut for PtrInt(Result) := PtrInt(P)+Add, without any range / overflow checks.
function GetClearMem(Size: integer; ClearValue: byte = 0): pointer; overload;
procedure FreeMemNiling(var p: pointer);
Safer version of FreeMem, checks is parameter Nil, and sets it to Nil afterwards.
function CheckIsMemCharFilled(const Data; Size: Integer; AChar: Char): Integer;
Check is memory filled with the given character. Returns -1 is True, or the number (between 0 .. Size - 1) or the first character different than AChar.
function IsMemCharFilled(const Data; Size: Integer; AChar: Char): boolean;
Check is memory filled with the given character. Returns simple boolean, use CheckIsMemCharFilled to get more informative result.
function IsMemWordFilled(const Data; Size: Integer; Value: Word): boolean;
Check is memory filled with the Word (2 byte sequence). Size if the number of words (not bytes), this is consequent with Size parameter from FillWord from FPC's RTL.
function IsMemDWordFilled(const Data; Size: Integer; Value: DWord): boolean;
Check is memory filled with the DWord (4 byte sequence). Size if the number of dwords (not bytes), this is consequent with Size parameter from FillDWord from FPC's RTL.
function Offset(var A, B): Pointer;
Calculate shift between A and B addresses (in bytes), and cast to Pointer. Useful to pass as offsets to OpenGL glVertexPointer and similar functions. This is simply Result := A - B, except we do some typecasting.
procedure ErrorWrite(const s: string); overload;
Write using a dialog box or console.
If we are a Windows GUI program (not IsConsole) then we use native Windows dialog boxes. Otherwise (a console is available, which is always true on non-Windows) we output the message using simple Writeln (to standard output for InfoWrite, or ErrOutput for ErrorWrite and WarningWrite).
procedure ErrorWrite(const s: string; const args: array of const); overload;
procedure WarningWrite(const s: string; const args: array of const); overload;
procedure InfoWrite(const s: string; const args: array of const); overload;
procedure InfoWriteParts(const TitleFormat: string; const Messages: array of string); deprecated;
Warning: this symbol is deprecated.
Output messages, using console or dialog box.
If we're not on Windows or IsConsole, then we simply output Messages using Writeln.
If we're on Windows and not IsConsole, then every Messages is displayed in a separate dialog box. Dialog box uses our InfoBox routine, with Messages[I] being message content and title being Format(TitleFormat, [I + 1, Messages.Count]).
This is good for outputting a lot of information.
Deprecated. This just looks ugly in GUI version. It's better to present long information using only a console (just use Writeln), or only a full-featured GUI (like Lazarus LCL or our CastleUIControls).
Types
TIsSmallerFunc = function (const A, B, Data: Pointer): boolean;
TIsSmallerFuncByObject = function (const A, B: Pointer): boolean of object;
Float = Math.Float;
PFloat = Math.PFloat;
PCardinal = ˆCardinal;
PLongWord = ˆLongWord;
PShortint = ˆShortint;
PBoolean = ˆBoolean;
Pointer to a boolean. Defined as ˆByte in some Delphi Windows unit, for FPC 1.0.x PBoolean is not available at all.
Pointer to TObject. Don't call this PObject or PTObject to avoid possible name clashes with other units (pointers are often used in situations that prevent good type-checking, so better to avoid name clashes to avoid some nasty errors).
TArray_Single = packed array [0..MaxInt div SizeOf(Single) - 1] of Single;
When should the complicated sorting algorithm fallback to a simpler one. If number of items is <= CountToUseSimpleSort, then Sort will fallback to SimpleSort (= sort by choosing for now) instead of recursive QuickSort. Set CountToUseSimpleSort = 0 to make it never fall back to SimpleSort.
By default this is DefaultCountToUseSimpleSort.
NL = LineEnding;
New line. Short name for LineEnding.
enatural = 2.71828182845905;
sqrt2 = 1.4142135623730950488016887242097;
Sqrt3 = 1.7320508075688773;
HalfPi = 1.57079632679489661923;
Half of Pi. Taken from FPC sources, file rtl/inc/genmath.inc
RootDir = '/'
;
Root dir name. Empty if not applicable to this OS.
ExeExtension = '' ;
Variables
BonusErrorMessg: string ='';
Additional message output when you end program with an exception.