435 lines
13 KiB
ObjectPascal
435 lines
13 KiB
ObjectPascal
/// wrapper for Windows functions translated to Linux for Kylix
|
|
unit SynKylix;
|
|
|
|
{
|
|
This file is part of Synopse mORMot framework.
|
|
|
|
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
|
|
Synopse Informatique - https://synopse.info
|
|
|
|
*** BEGIN LICENSE BLOCK *****
|
|
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
The contents of this file are subject to the Mozilla Public License Version
|
|
1.1 (the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
for the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is Synopse mORMot framework.
|
|
|
|
The Initial Developer of the Original Code is Arnaud Bouchez
|
|
|
|
Portions created by the Initial Developer are Copyright (C) 2022
|
|
the Initial Developer. All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
of those above. if you wish to allow use of your version of this file only
|
|
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
use your version of this file under the terms of the MPL, indicate your
|
|
decision by deleting the provisions above and replace them with the notice
|
|
and other provisions required by the GPL or the LGPL. if you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
}
|
|
|
|
{$I Synopse.inc} // define HASINLINE CPU32 CPU64 OWNNORMTOUPPER
|
|
|
|
interface
|
|
|
|
uses
|
|
LibC,
|
|
Types,
|
|
Classes,
|
|
SysUtils;
|
|
|
|
type
|
|
PSystemTime = ^TSystemTime;
|
|
/// System Time, as represented in FPC for POSIX systems
|
|
// - Windows.TSystemTime do have a "w" prefix, e.g. wYear vs Year below
|
|
TSystemTime = record
|
|
Year: Word;
|
|
Month: Word;
|
|
DayOfWeek: Word;
|
|
Day: Word;
|
|
Hour: Word;
|
|
Minute: Word;
|
|
Second: Word;
|
|
Millisecond: Word;
|
|
end;
|
|
|
|
cint = integer;
|
|
cuchar = byte;
|
|
cushort = word;
|
|
|
|
const
|
|
NOERROR = 0;
|
|
NO_ERROR = 0;
|
|
INVALID_HANDLE_VALUE = THandle(-1);
|
|
INFINITE = LongWord($FFFFFFFF);
|
|
|
|
LOCALE_USER_DEFAULT = $400;
|
|
NORM_IGNORECASE = 1;
|
|
|
|
FILE_BEGIN = SEEK_SET;
|
|
FILE_CURRENT = SEEK_CUR;
|
|
FILE_END = SEEK_END;
|
|
|
|
CLOCK_REALTIME = 0;
|
|
CLOCK_MONOTONIC = 1;
|
|
CLOCK_REALTIME_COARSE = 5;
|
|
CLOCK_MONOTONIC_COARSE = 6; // see http://lwn.net/Articles/347811
|
|
|
|
var
|
|
// contains CLOCK_REALTIME_COARSE since kernel 2.6.32
|
|
CLOCK_REALTIME_TICKCOUNT: integer = CLOCK_REALTIME;
|
|
// contains CLOCK_MONOTONIC_COARSE since kernel 2.6.32
|
|
CLOCK_MONOTONIC_TICKCOUNT: integer = CLOCK_MONOTONIC;
|
|
|
|
/// compatibility function, wrapping Win32 API high resolution timer
|
|
// - this version will return the CLOCK_MONOTONIC value, with a 1 ns resolution
|
|
procedure QueryPerformanceCounter(var Value: Int64);
|
|
|
|
/// slightly faster than QueryPerformanceCounter() div 1000 - but not for Windows
|
|
procedure QueryPerformanceMicroSeconds(out Value: Int64);
|
|
|
|
/// compatibility function, wrapping Win32 API high resolution timer
|
|
function QueryPerformanceFrequency(var Value: Int64): boolean;
|
|
|
|
/// compatibility function, wrapping Win32 API file position change
|
|
function SetFilePointer(hFile: THandle; lDistanceToMove: integer;
|
|
lpDistanceToMoveHigh: Pointer; dwMoveMethod: integer): dword;
|
|
|
|
/// compatibility function, wrapping Win32 API file truncate at current position
|
|
procedure SetEndOfFile(hFile: integer);
|
|
|
|
/// compatibility function, wrapping Win32 API file flush to disk
|
|
procedure FlushFileBuffers(hFile: THandle);
|
|
|
|
/// compatibility function, wrapping Win32 API text comparison
|
|
function CompareStringW(GetThreadLocale: DWORD; dwCmpFlags: DWORD; lpString1: Pwidechar;
|
|
cchCount1: longint; lpString2: Pwidechar; cchCount2: longint): longint;
|
|
|
|
/// a wrapper around stat() to retrieve a file size
|
|
function GetFileSize(hFile: THandle; lpFileSizeHigh: PDWORD): DWORD;
|
|
|
|
/// returns the current UTC time
|
|
function GetNowUTC: TDateTime;
|
|
|
|
/// returns the current UTC time, as Unix Epoch seconds
|
|
function GetUnixUTC: Int64;
|
|
|
|
/// returns the current UTC time, as Unix Epoch milliseconds
|
|
// - will call clock_gettime(CLOCK_REALTIME_COARSE) if available
|
|
function GetUnixMSUTC: Int64;
|
|
|
|
/// returns the current UTC time as TSystemTime
|
|
procedure GetNowUTCSystem(var result: TSystemTime);
|
|
|
|
/// compatibility function, to be implemented according to the running OS
|
|
// - expect more or less the same result as the homonymous Win32 API function
|
|
// - will use CLOCK_MONOTONIC_COARSE, available since Linux 2.6.32, which
|
|
// is very fast, and has a 1 ms resolution
|
|
function GetTickCount64: Int64;
|
|
|
|
/// compatibility function, to be implemented according to the running OS
|
|
// - expect more or less the same result as the homonymous Win32 API function
|
|
function GetTickCount: cardinal;
|
|
|
|
/// compatibility function, to be implemented according to the running OS
|
|
procedure GetLocalTime(var result: TSystemTime);
|
|
|
|
/// compatibility function, available in SysUtils.pas only for Windows
|
|
function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
|
|
|
|
/// wrapper around libiconv buffer conversion
|
|
// - will write any invalid chars as '?'
|
|
// - returns the final Dest position
|
|
function IconvBufConvert(context: iconv_t;
|
|
Src: pointer; SrcBytes, SrcCharSize: Integer;
|
|
Dest: pointer; DestBytes, DestCharSize: integer): pointer;
|
|
|
|
/// similar to Windows sleep() API call, to be truly cross-platform
|
|
// - it should have a millisecond resolution, and handle ms=0 as a switch to
|
|
// another pending thread, i.e. call sched_yield() API
|
|
procedure SleepHiRes(ms: cardinal);
|
|
|
|
/// similar to FPC's unix.GetHostName function
|
|
function GetHostName: string;
|
|
|
|
var
|
|
/// will contain the current Linux kernel revision, as one integer
|
|
// - e.g. $030d02 for 3.13.2, or $020620 for 2.6.32
|
|
KernelRevision: cardinal;
|
|
|
|
|
|
implementation
|
|
|
|
const
|
|
C_THOUSAND = Int64(1000);
|
|
C_MILLION = Int64(C_THOUSAND * C_THOUSAND);
|
|
C_BILLION = Int64(C_THOUSAND * C_THOUSAND * C_THOUSAND);
|
|
|
|
procedure QueryPerformanceCounter(var Value: Int64);
|
|
var r: TTimeSpec;
|
|
begin
|
|
clock_gettime(CLOCK_MONOTONIC,r);
|
|
value := r.tv_nsec+r.tv_sec*C_BILLION; // nanosecond resolution
|
|
end;
|
|
|
|
procedure QueryPerformanceMicroSeconds(out Value: Int64);
|
|
var r : TTimeSpec;
|
|
begin
|
|
clock_gettime(CLOCK_MONOTONIC,r);
|
|
value := r.tv_nsec div 1000+r.tv_sec*C_MILLION;
|
|
end;
|
|
|
|
function QueryPerformanceFrequency(var Value: Int64): boolean;
|
|
begin
|
|
Value := C_BILLION; // 1 second = 1e9 nanoseconds
|
|
result := true;
|
|
end;
|
|
|
|
function SetFilePointer(hFile: THandle; lDistanceToMove: integer;
|
|
lpDistanceToMoveHigh: Pointer; dwMoveMethod: integer): dword;
|
|
var offs: Int64;
|
|
begin
|
|
Int64Rec(Offs).Lo := lDistanceToMove;
|
|
if lpDistanceToMoveHigh=nil then
|
|
Int64Rec(Offs).Hi := 0 else
|
|
Int64Rec(Offs).Hi := PDWord(lpDistanceToMoveHigh)^;
|
|
offs := lseek64(hFile,offs,dwMoveMethod);
|
|
result := Int64Rec(offs).Lo;
|
|
if lpDistanceToMoveHigh<>nil then
|
|
PDWord(lpDistanceToMoveHigh)^ := Int64Rec(offs).Hi;
|
|
end;
|
|
|
|
procedure SetEndOfFile(hFile: integer);
|
|
begin
|
|
ftruncate64(hFile,lseek64(hFile,0,SEEK_CUR));
|
|
end;
|
|
|
|
procedure FlushFileBuffers(hFile: THandle);
|
|
begin
|
|
fsync(hFile);
|
|
end;
|
|
|
|
function CompareStringW(GetThreadLocale: DWORD; dwCmpFlags: DWORD; lpString1: Pwidechar;
|
|
cchCount1: longint; lpString2: Pwidechar; cchCount2: longint): longint;
|
|
var W1,W2: WideString;
|
|
begin
|
|
W1 := lpString1;
|
|
W2 := lpString2;
|
|
if dwCmpFlags and NORM_IGNORECASE<>0 then
|
|
result := WideCompareText(W1,W2) else
|
|
result := WideCompareStr(W1,W2);
|
|
end;
|
|
|
|
function GetFileSize(hFile: THandle; lpFileSizeHigh: PDWORD): DWORD;
|
|
var current,size: Int64;
|
|
begin // fstat64() returns error EBADF depending on open flags -> use lseek64
|
|
current := lseek64(hFile,0,SEEK_CUR);
|
|
size := lseek64(hFile,0,SEEK_END);
|
|
lseek64(hFile,current,SEEK_SET);
|
|
result := Int64Rec(size).Lo;
|
|
if lpFileSizeHigh<>nil then
|
|
lpFileSizeHigh^ := Int64Rec(size).Hi;
|
|
end;
|
|
|
|
const
|
|
HoursPerDay = 24;
|
|
MinsPerHour = 60;
|
|
SecsPerMin = 60;
|
|
SecsPerHour = MinsPerHour*SecsPerMin;
|
|
SecsPerDay = HoursPerDay*SecsPerHour;
|
|
C1970 = 2440588;
|
|
D0 = 1461;
|
|
D1 = 146097;
|
|
D2 = 1721119;
|
|
|
|
procedure JulianDaysNumberToGregorian(JulianDN: integer; out Year,Month,Day: Word);
|
|
var YYear,XYear,Temp,TempMonth: integer;
|
|
begin // see http://en.wikipedia.org/wiki/Julian_day
|
|
Temp := ((JulianDN-D2) shl 2)-1;
|
|
JulianDN := Temp div D1;
|
|
XYear := (Temp mod D1) or 3;
|
|
YYear := (XYear div D0);
|
|
Temp := ((((XYear mod D0)+4) shr 2)*5)-3;
|
|
Day := ((Temp mod 153)+5) div 5;
|
|
TempMonth := Temp div 153;
|
|
if TempMonth>=10 then begin
|
|
inc(YYear);
|
|
dec(TempMonth,12);
|
|
end;
|
|
inc(TempMonth,3);
|
|
Month := TempMonth;
|
|
Year := YYear+(JulianDN*100);
|
|
end;
|
|
|
|
procedure TimeValToSystemTime(const tz: TTimeVal; var result: TSystemTime);
|
|
var days, secs: integer;
|
|
begin
|
|
days := tz.tv_sec div SecsPerDay;
|
|
JulianDaysNumberToGregorian(days+c1970,result.Year,result.Month,result.Day);
|
|
secs := abs(tz.tv_sec mod SecsPerDay);
|
|
result.hour := secs div SecsPerHour;
|
|
secs := secs mod SecsPerHour;
|
|
result.minute := secs div SecsPerMin;
|
|
result.second := secs mod SecsPerMin;
|
|
result.MilliSecond := tz.tv_usec div 1000;
|
|
end;
|
|
|
|
function GetNowUTC: TDateTime;
|
|
var SystemTime: TSystemTime;
|
|
begin
|
|
GetNowUTCSystem(SystemTime);
|
|
result := SystemTimeToDateTime(SystemTime);
|
|
end;
|
|
|
|
function GetUnixUTC: Int64;
|
|
var r: TTimeSpec;
|
|
begin
|
|
clock_gettime(CLOCK_REALTIME_TICKCOUNT,r); // faster than gettimeofday
|
|
result := r.tv_sec;
|
|
end;
|
|
|
|
function GetUnixMSUTC: Int64;
|
|
var r: TTimeSpec;
|
|
begin
|
|
clock_gettime(CLOCK_REALTIME_TICKCOUNT,r);
|
|
result := Int64(r.tv_sec)*C_THOUSAND+(cardinal(r.tv_nsec) div 1000000); // in ms
|
|
end;
|
|
|
|
procedure GetNowUTCSystem(var result: TSystemTime);
|
|
var tz: TTimeVal;
|
|
begin
|
|
gettimeofday(tz,nil);
|
|
TimeValToSystemTime(tz,result);
|
|
end;
|
|
|
|
procedure GetLocalTime(var result: TSystemTime);
|
|
var tv: TTimeVal;
|
|
ut: TUnixTime;
|
|
t: TTime_T;
|
|
begin
|
|
gettimeofday(tv,nil);
|
|
t := tv.tv_sec;
|
|
localtime_r(@t,ut);
|
|
result.Year := ut.tm_year+1900;
|
|
result.Month := ut.tm_mon+1;
|
|
result.Day := ut.tm_mday;
|
|
result.Hour := ut.tm_hour;
|
|
result.Minute := ut.tm_min;
|
|
result.Second := ut.tm_sec;
|
|
result.MilliSecond := tv.tv_usec div 1000;
|
|
end;
|
|
|
|
function GetTickCount64: Int64;
|
|
var r: TTimeSpec;
|
|
begin
|
|
clock_gettime(CLOCK_MONOTONIC_TICKCOUNT,r);
|
|
result := Int64(r.tv_sec)*C_THOUSAND+(cardinal(r.tv_nsec) div 1000000); // in ms
|
|
end;
|
|
|
|
function GetTickCount: cardinal;
|
|
begin
|
|
result := cardinal(GetTickCount64);
|
|
end;
|
|
|
|
function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
|
|
var time: TDateTime;
|
|
begin
|
|
with SystemTime do begin
|
|
result := EncodeDate(Year,Month,Day);
|
|
time := EncodeTime(Hour,Minute,Second,MilliSecond);
|
|
if result>=0 then
|
|
result := result+time else
|
|
result := result-time;
|
|
end;
|
|
end;
|
|
|
|
function IconvBufConvert(context: iconv_t;
|
|
Src: pointer; SrcBytes, SrcCharSize: Integer;
|
|
Dest: pointer; DestBytes, DestCharSize: integer): pointer;
|
|
var SrcBytesLeft, DestBytesLeft, Zero: size_t;
|
|
LastError: Integer;
|
|
pNil: pointer;
|
|
begin
|
|
DestBytesLeft := DestBytes;
|
|
SrcBytesLeft := SrcBytes;
|
|
repeat
|
|
if LibC.iconv(context,PChar(Src),SrcBytesLeft,Dest,DestBytesLeft)<>size_t(-1) then
|
|
break; // success
|
|
LastError := GetLastError;
|
|
if (LastError=E2BIG) and (SrcBytesLeft>0) and (DestBytesLeft>0) then
|
|
continue;
|
|
if (LastError<>EINVAL) and (LastError<>EILSEQ) then
|
|
raise Exception.CreateFmt('SynKylix: iconv() fatal error %d',[LastError]);
|
|
Zero := 0;
|
|
pNil := nil;
|
|
LibC.iconv(context,PChar(pNil),Zero,pNil,Zero); // reset
|
|
inc(PByte(Src),SrcCharSize);
|
|
dec(SrcBytesLeft,SrcCharSize);
|
|
PWord(Dest)^ := ord('?');
|
|
inc(PByte(Dest),DestCharSize);
|
|
dec(DestBytesLeft,DestCharSize);
|
|
until false;
|
|
result := Dest;
|
|
end;
|
|
|
|
procedure SleepHiRes(ms: cardinal);
|
|
begin
|
|
if ms=0 then
|
|
usleep(1) else // sched_yield() is buggy on multi CPU
|
|
usleep(ms shl 10); // from ms to us
|
|
end;
|
|
|
|
function GetHostName: string;
|
|
var tmp: array[byte] of AnsiChar;
|
|
begin
|
|
LibC.gethostname(tmp,sizeof(tmp)-1);
|
|
result := tmp;
|
|
end;
|
|
|
|
procedure GetKernelRevision;
|
|
var uts: UtsName;
|
|
P: PByte;
|
|
function GetNext: cardinal;
|
|
var c: cardinal;
|
|
begin
|
|
result := 0;
|
|
repeat
|
|
c := P^-48;
|
|
if c>9 then
|
|
break else
|
|
result := result*10+c;
|
|
inc(P);
|
|
until false;
|
|
if P^=ord('.') then
|
|
inc(P);
|
|
end;
|
|
begin
|
|
uname(uts);
|
|
P := @uts.release;
|
|
KernelRevision := GetNext shl 16+GetNext shl 8+GetNext;
|
|
if KernelRevision>=$020620 then begin // expects kernel 2.6.32 or higher
|
|
CLOCK_MONOTONIC_TICKCOUNT := CLOCK_MONOTONIC_COARSE;
|
|
CLOCK_REALTIME_TICKCOUNT := CLOCK_REALTIME_COARSE;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
GetKernelRevision;
|
|
end.
|